转发:https://www.iteye.com/blog/elim-2395337

5 给Advice传递参数

Advice除了可以接收JoinPoint(非Around Advice)或ProceedingJoinPoint(Around Advice)参数外,还可以直接接收与切入点方法执行有关的对象,比如切入点方法参数、切入点目标对象(target)、切入点代理对象(this)等。

5.1 获取切入点方法参数

假设我们现在有一个id为userService的bean中定义了一个findById(int id)方法,我们希望定义一个Advice来拦截这个方法,并且把findById()的参数作为Advice处理方法的参数,即每次调用findById()传递的参数都将传递到Advice处理方法,那么我们可以如下这样定义。

@Before(value="bean(userService) && execution(* findById(java.lang.Integer)) && args(id)", argNames="id")
public void beforeWithParam(JoinPoint joinPoint, Integer id) {
System.out.println(this.getClass().getName()+" ID is : " + id);
}

上面这种定义是非常精确的定义,我们通过表达式“bean(userService) && execution(* findById(java.lang.Integer))”就已经明确的指定了我们需要拦截的是id或name为userService的findById(Integer)方法,后面又加了一个args(id)是干什么用的呢?它的作用跟findById(Integer)是类似的,它表示我们的切入点方法必须只接收一个参数,而且这个参数的类型是和当前定义的Advice处理方法的参数id是相同类型的,在上面的示例中其实就是要求是Integer类型的;另外它还有一个非常重要的作用,通过这种指定后对应的Advice处理方法在执行时将接收到与之对应的切入点方法参数的值。在上面的示例中笔者特意给Advice处理方法加了一个JoinPoint参数是为了说明JoinPoint、ProceedingJoinPoint参数是可以直接定义在Advice方法的第一个参数,并且是可以与其它接收的参数共存的。其实如果我们不只是需要拦截findById(Integer)方法,而是需要拦截id为userService的bean中所有接收一个int/Integer参数的方法,那么我们可以把上面的配置简化为如下这样。

@Before(value="bean(userService) && args(id)", argNames="id")
public void beforeWithParam2(int id) {
System.out.println(this.getClass().getName()+" ID is : " + id);
}

如果我们需要拦截的方法可能是有多个参数的,但我们只关注第一个参数,那我们可以把表达式调整为如下这样,只关注第一个参数为int/Integer类型的,并且在Advice方法中接收这个方法参数进行相应的处理。

@Before(value="bean(userService) && args(id,..)", argNames="id")
public void beforeWithParam2(int id) {
System.out.println(this.getClass().getName()+" ID is : " + id);
}

5.2 argNames参数

我们可以看到在上述例子中我们都指定了@Before的argNames属性的值为id,那么这个argNames属性有什么作用呢?argNames属性是用于指定在表达式中应用的参数名与Advice方法参数是如何对应的,argNames中指定的参数名必须与表达式中的一致,可以与Advice方法参数名不一致;当表达式中使用了多个参数时,argNames中需要指定多个参数,多个参数之间以英文逗号分隔,这些参数的顺序必须与对应的Advice方法定义的参数顺序是一致的。比如下面这个示例中,我们在Pointcut表达式中使用了name和sex两个参数,我们的Advice处理方法接收两个参数,分别是sex1和name1,我们希望Pointcut表达式中的name参数是对应的Advice处理方法的第二个参数,即name1,希望Pointcut表达式中的sex参数是对应的Advice处理方法的第一个参数,即sex1,那么我们在指定@Before注解的argNames参数时必须定义name和sex参数与Advice处理方法参数的关系,且顺序要求与对应的处理方法的参数顺序一致,即哪个参数是需要与Advice处理方法的第一个参数匹配则把哪个参数放第一位,与第二个参数匹配的则放第二位,在我们的这个示例中就应该是sex放第一位,name放第二位。

@Before(value="bean(userService) && args(name, sex)", argNames="sex, name")
public void beforeWithParam3(int sex1, String name1) {
System.out.println("sex is : " + sex1);
System.out.println("name is : " + name1);
}

@Before注解的argNames参数不是必须的,它只有在我们编译的字节码中不含DEBUG信息或Pointcut表达式中使用的参数名与Advice处理方法的参数名不一致时才需要。所以在编译的字节码中包含DEBUG信息且Advice参数名与Pointcut表达式中使用的参数名一致时,我们完全可以把argNames参数省略。如果表达式里面使用了多个参数,那么这些参数在表达式中的顺序可以与Advice方法对应参数的顺序不一致,例如下面这个样子。

@Before(value="bean(userService) && args(id)")
public void beforeWithParam2(int id) {
System.out.println(this.getClass().getName()+" ID is : " + id);
}

5.3 获取this对象

this对象就是Spring生成的bean的那个代理对象。如下示例就是Advice方法接收this对象,我们给Advice方法指定一个需要拦截的this对象类型的参数,然后在表达式中使用this类型的表达式定义,表达式中定义的对应类型指定为Advice方法参数。

@Before("this(userService)")
public void beforeWithParam4(IUserService userService) {
//this对象应该是一个代理对象
System.out.println(this.getClass().getName()+"==============传递this对象: " + userService.getClass());
}

5.4 混合使用

我们的Advice方法可以同时接收多个目标方法参数,与此同时它也可以接收this等对象,即它们是可以混合使用的。下面这个示例中我们就同时接收了this对象和目标方法int/Interger类型的参数。

@Before("this(userService) && args(id)")
public void beforeWithParam5(IUserService userService, int id) {
System.out.println(this.getClass().getName()+"===========" + id + "==============" + userService.getClass());
}

5.5 获取target对象

获取target对象也比较简单,只需要把表达式改为target类型的表达式即可。

@Before("target(userService)")
public void beforeWithParam6(IUserService userService) {
System.out.println(this.getClass().getName()+"==============传递target对象: " + userService.getClass());
}

5.6 获取注解对象

当我们的Pointcut表达式类型是通过注解匹配时,我们也可以在Advice处理方法中获取匹配的注解对象,如下面这个示例,其它如使用@target等是类似的。

@Before("@annotation(annotation)")
public void beforeWithParam7(MyAnnotation annotation) {
System.out.println(this.getClass().getName()+"==============传递标注在方法上的annotation: " + annotation.annotationType().getName());
}

5.7 泛型参数

有的时候我们的Advice方法需要接收的切入点方法参数定义的不是具体的类型,而是一个泛型,这种情况下怎么办呢?可能你会想那我就把对应的Advice方法参数定义为Object类型就好了,反正所有的类型都可以转换为Object类型。对的,这样是没有错的,但是说如果你只想拦截某种具体类型的参数调用时就可以不用把Advice方法参数类型定义为Object了,这样还得在方法体里面进行判断,我们可以直接把Advice方法参数类型定义为我们想拦截的方法参数类型。比如我们有下面这样一个使用了泛型的方法定义,我们希望只有在调用testParam方法时传递的参数类型是Integer类型时才进行拦截。

	public <T> void testParam(T param);

那这个时候我们就可以把我们的Advice的表达式定义为如下这样,前者精确定义接收方法名为testParam,返回类型为void,后者精确定义方法参数为一个Integer类型的参数,其实前者也可以定义为“execution(void testParam(Integer))”。看到这你可能会想,为什么不直接把表达式定义为“execution(void testParam(param))”呢?因为execution是不支持Advice方法参数绑定的,基本上支持Advice参数绑定的就只有this、target、args以及对应的注解形式加@annotation。

@Before("execution(void testParam(..)) && args(param)")
public void beforeWithParam8(Integer param) {
System.out.println("pointcut expression[args(param)]--------------param:" + param);
}

以上就是常用的传递参数给Advice处理方法的方式,有一些示例可能没有讲到,比如@target这种,这些其实都是类似的。包括上面我们都是以@Before这种Advice来讲的,其实其它的Advice在接收参数的时候也是类似的。

参考文档
1、官方文档

(注:本文是基于Spring4.1.0所写,写于2017年1月20日星期五)

Spring Aop(五)——给Advice传参的更多相关文章

  1. Spring笔记07(Spring AOP的通知advice和顾问advisor)

    1.Spring AOP的通知advice 01.接口代码: package cn.pb.dao; public interface UserDao { //主业务 String add(); //主 ...

  2. Spring学习(十五)----- Spring AOP通知实例 – Advice

    Spring AOP(面向方面编程)框架,用于在模块化方面的横切关注点.简单得说,它只是一个拦截器拦截一些过程,例如,当一个方法执行,Spring AOP 可以劫持一个执行的方法,在方法执行之前或之后 ...

  3. Spring AOP项目应用——方法入参校验 & 日志横切

    转载:https://blog.csdn.net/Daybreak1209/article/details/80591566 应用一:方法入参校验 由于系统多个方法入参均对外封装了统一的Dto,其中D ...

  4. Spring AOP通知实例 – Advice

    Spring AOP(面向方面编程)框架,用于在模块化方面的横切关注点.简单得说,它只是一个拦截器拦截一些过程,例如,当一个方法执行,Spring AOP 可以劫持一个执行的方法,在方法执行之前或之后 ...

  5. Spring中RedirectAttributes对象重定向传参

    Spring3中的FlashAttribute 为 了防止用户刷新重复提交,save操作之后一般会redirect到另一个页面,同时带点操作成功的提示信息.因为是Redirect,Request里 的 ...

  6. DRF框架(五)——context传参,二次封装Response类,两个视图基类(APIView/GenericAPIView),视图扩展类(mixins),子类视图(工具视图),视图集(viewsets),工具视图集

    复习 1.整体修改与局部修改 # 序列化get (给前端传递参数) #查询 ser_obj = ModelSerializer(model_obj) #只传递一个参数,默认是instance的参数,查 ...

  7. Spring技术内幕:Spring AOP的实现原理(五)

    7.Advice通知的实现 AopProxy代理对象生成时,其拦截器也一并生成.以下我们来分析下Aop是怎样对目标对象进行增强的.在为AopProxy配置拦截器的实现中,有一个取得拦截器配置过程,这个 ...

  8. [转]彻底征服 Spring AOP 之 理论篇

    基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹的是, 这些概念经过了中文翻译后, 变得面目全非, 相同的一个术语, 在不同的翻译下, ...

  9. 彻底征服 Spring AOP 之 理论篇

    基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹的是, 这些概念经过了中文翻译后, 变得面目全非, 相同的一个术语, 在不同的翻译下, ...

随机推荐

  1. WCF错误处理

    介绍 WCF(Windows Communication Foundation) -异常处理:一般Exception的处理,FaultException和FaultException<T> ...

  2. v-model原理解析

    vue中v-model可以实现数据的双向绑定,但是为什么这个指令就可以实现数据的双向绑定呢? 其实v-model是vue的一个语法糖.即利用v-model绑定数据后,既绑定了数据,又添加了一个inpu ...

  3. FFmpeg常用命令学习笔记(六)图片与视频互转命令

    图片与视频互转命令 1.视频转图片 ffmpeg -i in.mp4 -r 1 -f image2 img-%3d.jpeg -r 1:转换图片帧率为1,也就是1秒转1张.-f image2:将媒体文 ...

  4. C# 安全性

    一.标识和Principal static void Main(string[] args) { AppDomain.CurrentDomain.SetPrincipalPolicy(System.S ...

  5. 一些有用的dll

    1.生成excel工具- EPPlus EPPlus.dll 2.生成word工具 - OpenXml DocumentFormat.OpenXml.dll 3.生成条形码工具 -  ZXing zx ...

  6. Codevs 1038 一元三次方程求解 NOIP 2001(导数 牛顿迭代)

    1038 一元三次方程求解 2001年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题目描述 Description 有形如:ax3+b ...

  7. Python中greenlet和gevent使用示例

    目录 greenlet示例 示例1,线程切换 示例2 gevent 示例1 示例2: gevent使用monkey对所有系统自带的IO操作打patch 示例3,发送请求 示例4:使用gevent的so ...

  8. 八、linux文件系统上的特殊权限 SUID 、GUID、Sticky

    安全上下文 前提:进程有属主和属组,文件有属主和属组 任何一个可执行程序文件能不能启动为进程:取决发起者对程序文件是否拥有执行权限 启动为进程之后,其进程的属主为发起者:进程的属组为发起者所属的组 进 ...

  9. TCP首部的TimeStamp时间戳选项 转载

    TCP应该是以太网协议族中被应用最为广泛的协议之中的一个,这里就聊一聊TCP协议中的TimeStamp选项.这个选项是由RFC 1323引入的,该C建议提交于1992年.到今天已经足足有20个年头.只 ...

  10. Sketch教程

    去年夏天开始用Sketch,觉得它放Dock里闪亮亮的很好看,当时笑称是男盆友送我的第一颗小钻石噗哈哈.所以那段时间几乎刷遍了所有关于 Sketch 的网站.文章.教程,之后又在自学设计,因为想和更多 ...