Spring中的AOP(四)——在Advice方法中获取目标方法的参数

2024-04-27 11:58

本文主要是介绍Spring中的AOP(四)——在Advice方法中获取目标方法的参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

获取目标方法的信息

    访问目标方法最简单的做法是定义增强处理方法时,将第一个参数定义为JoinPoint类型,当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强处理的连接点。JoinPoint里包含了如下几个常用的方法:

  • Object[] getArgs:返回目标方法的参数

  • Signature getSignature:返回目标方法的签名

  • Object getTarget:返回被织入增强处理的目标对象

  • Object getThis:返回AOP框架为目标对象生成的代理对象

    注意:当使用@Around处理时,我们需要将第一个参数定义为ProceedingJoinPoint类型,该类是JoinPoint的子类。

    下面的切面类(依然放在com.abc.advice包中)中定义了Before、Around、AfterReturning和After 4中增强处理,并分别在4种增强处理中访问被织入增强处理的目标方法、目标方法的参数和被织入增强处理的目标对象等:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package  com.abc.advice;
import  java.util.Arrays;
import  org.aspectj.lang.JoinPoint;
import  org.aspectj.lang.ProceedingJoinPoint;
import  org.aspectj.lang.annotation.After;
import  org.aspectj.lang.annotation.AfterReturning;
import  org.aspectj.lang.annotation.Around;
import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Before;
@Aspect
public  class  AdviceTest {
     @Around ( "execution(* com.abc.service.*.many*(..))" )
     public  Object process(ProceedingJoinPoint point)  throws  Throwable {
         System.out.println( "@Around:执行目标方法之前..." );
         //访问目标方法的参数:
         Object[] args = point.getArgs();
         if  (args !=  null  && args.length >  0  && args[ 0 ].getClass() == String. class ) {
             args[ 0 ] =  "改变后的参数1" ;
         }
         //用改变后的参数执行目标方法
         Object returnValue = point.proceed(args);
         System.out.println( "@Around:执行目标方法之后..." );
         System.out.println( "@Around:被织入的目标对象为:"  + point.getTarget());
         return  "原返回值:"  + returnValue +  ",这是返回结果的后缀" ;
     }
     
     @Before ( "execution(* com.abc.service.*.many*(..))" )
     public  void  permissionCheck(JoinPoint point) {
         System.out.println( "@Before:模拟权限检查..." );
         System.out.println( "@Before:目标方法为:" 
                 point.getSignature().getDeclaringTypeName() + 
                 "."  + point.getSignature().getName());
         System.out.println( "@Before:参数为:"  + Arrays.toString(point.getArgs()));
         System.out.println( "@Before:被织入的目标对象为:"  + point.getTarget());
     }
     
     @AfterReturning (pointcut= "execution(* com.abc.service.*.many*(..))"
         returning= "returnValue" )
     public  void  log(JoinPoint point, Object returnValue) {
         System.out.println( "@AfterReturning:模拟日志记录功能..." );
         System.out.println( "@AfterReturning:目标方法为:" 
                 point.getSignature().getDeclaringTypeName() + 
                 "."  + point.getSignature().getName());
         System.out.println( "@AfterReturning:参数为:" 
                 Arrays.toString(point.getArgs()));
         System.out.println( "@AfterReturning:返回值为:"  + returnValue);
         System.out.println( "@AfterReturning:被织入的目标对象为:"  + point.getTarget());
         
     }
     
     @After ( "execution(* com.abc.service.*.many*(..))" )
     public  void  releaseResource(JoinPoint point) {
         System.out.println( "@After:模拟释放资源..." );
         System.out.println( "@After:目标方法为:" 
                 point.getSignature().getDeclaringTypeName() + 
                 "."  + point.getSignature().getName());
         System.out.println( "@After:参数为:"  + Arrays.toString(point.getArgs()));
         System.out.println( "@After:被织入的目标对象为:"  + point.getTarget());
     }
}

    在AdviceManager类中增加以下内容:

?
1
2
3
4
5
//将被AdviceTest的各种方法匹配
public  String manyAdvices(String param1, String param2) {
     System.out.println( "方法:manyAdvices" );
     return  param1 +  " 、"  + param2;
}

    在com.abc.main.AOPTest中加入方法的调用,触发切点:

?
1
2
String result = manager.manyAdvices( "aa" "bb" );
System.out.println( "Test方法中调用切点方法的返回值:"  + result);

    下面是执行结果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Around :执行目标方法之前...
@Before :模拟权限检查...
@Before :目标方法为:com.abc.service.AdviceManager.manyAdvices
@Before :参数为:[改变后的参数 1 , bb]
@Before :被织入的目标对象为:com.abc.service.AdviceManager @1dfc617e
方法:manyAdvices
@Around :执行目标方法之后...
@Around :被织入的目标对象为:com.abc.service.AdviceManager @1dfc617e
@After :模拟释放资源...
@After :目标方法为:com.abc.service.AdviceManager.manyAdvices
@After :参数为:[改变后的参数 1 , bb]
@After :被织入的目标对象为:com.abc.service.AdviceManager @1dfc617e
@AfterReturning :模拟日志记录功能...
@AfterReturning :目标方法为:com.abc.service.AdviceManager.manyAdvices
@AfterReturning :参数为:[改变后的参数 1 , bb]
@AfterReturning :返回值为:原返回值:改变后的参数 1  、 bb,这是返回结果的后缀
@AfterReturning :被织入的目标对象为:com.abc.service.AdviceManager @1dfc617e
Test方法中调用切点方法的返回值:原返回值:改变后的参数 1  、bb,这是返回结果的后缀

    从结果中可以看出:在任何一个织入的增强处理中,都可以获取目标方法的信息。另外,spring AOP采用和AspectJ一样的有限顺序来织入增强处理:在“进入”连接点时,最高优先级的增强处理将先被织入(所以给定的两个Before增强处理中,优先级高的那个会先执行);在“退出”连接点时,最高优先级的增强处理会最后被织入(所以给定的两个After增强处理中,优先级高的那个会后执行)。当不同的切面中的多个增强处理需要在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这些增强处理。如果应用需要指定不同切面类里的增强处理的优先级,Spring提供了如下两种解决方案:

  • 让切面类实现org.springframework.core.Ordered接口:实现该接口只需要实现一个int getOrder()方法,该方法返回值越小,优先级越高

  • 直接使用@Order注解来修饰一个切面类:使用这个注解时可以配置一个int类型的value属性,该属性值越小,优先级越高

    优先级高的切面类里的增强处理的优先级总是比优先级低的切面类中的增强处理的优先级高。例如:优先级为1的切面类Bean1包含了@Before,优先级为2的切面类Bean2包含了@Around,虽然@Around优先级高于@Before,但由于Bean1的优先级高于Bean2的优先级,因此Bean1中的@Before先被织入。

    同一个切面类里的两个相同类型的增强处理在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这两个增强处理,没有办法指定它们的织入顺序。如果确实需要保证它们以固有的顺序被织入,则可以考虑将多个增强处理压缩为一个增强处理;或者将不同增强处理重构到不同切面中,通过在切面级别上定义顺序。

    如果只要访问目标方法的参数,Spring还提供了一种更加简洁的方法:我们可以在程序中使用args来绑定目标方法的参数。如果在一个args表达式中指定了一个或多个参数,该切入点将只匹配具有对应形参的方法,且目标方法的参数值将被传入增强处理方法。下面辅以例子说明:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  com.abc.advice;
import  java.util.Date;
import  org.aspectj.lang.annotation.AfterReturning;
import  org.aspectj.lang.annotation.Aspect;
@Aspect
public  class  AccessArgAdviceTest {
     @AfterReturning (
             pointcut= "execution(* com.abc.service.*.access*(..)) && args(time, name)" ,
             returning= "returnValue" )
     public  void  access(Date time, Object returnValue, String name) {
         System.out.println( "目标方法中的参数String = "  + name);
         System.out.println( "目标方法中的参数Date = "  + time);
         System.out.println( "目标方法的返回结果returnValue = "  + returnValue);
     }
}

    上面的程序中,定义pointcut时,表达式中增加了args(time, name)部分,意味着可以在增强处理方法(access方法)中定义time和name两个属性——这两个形参的类型可以随意指定,但一旦指定了这两个参数的类型,则这两个形参类型将用于限制该切入点只匹配第一个参数类型为Date,第二个参数类型为name的方法(方法参数个数和类型若有不同均不匹配)。

    注意,在定义returning的时候,这个值(即上面的returning="returnValue"中的returnValue)作为增强处理方法的形参时,位置可以随意,即:如果上面access方法的签名可以为

?
1
public  void  access(Date time, Object returnValue, String name)

    也可以为

?
1
public  void  access(Object returnValue, Date time, String name)

    还可以为

?
1
public  void  access(Date time, String name, Object returnValue)

    只需要满足另外的参数名的顺序和pointcut中args(param1, param2)的顺序相同即可。我们在AdviceManager中定义一个方法,该方法的第一个参数为Date类型,第二个参数为String类型,该方法的执行将触发上面的access方法,如下:

?
1
2
3
4
5
//将被AccessArgAdviceTest的access方法匹配
public  String accessAdvice(Date d, String n) {
     System.out.println( "方法:accessAdvice" );
     return  "aa" ;
}

    在AOPTest中增加调用这个accessAdvice方法并执行,下面是输出结果:

    从执行结果可以看出,使用args表达式有如下两个作用:

  • 提供了一种简单的方式来访问目标方法的参数

  • 可用于对切入点表达式作额外的限制

    除此之外,使用args表达式时,还可以使用如下形式:args(param1, param2, ..),注意args参数中后面的两个点,它表示可以匹配更多参数。在例子args(param1, param2, ..)中,表示目标方法只需匹配前面param1和param2的类型即可。

这篇关于Spring中的AOP(四)——在Advice方法中获取目标方法的参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/940444

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Git中恢复已删除分支的几种方法

《Git中恢复已删除分支的几种方法》:本文主要介绍在Git中恢复已删除分支的几种方法,包括查找提交记录、恢复分支、推送恢复的分支等步骤,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录1. 恢复本地删除的分支场景方法2. 恢复远程删除的分支场景方法3. 恢复未推送的本地删除分支场景方法4. 恢复

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

Window Server2016加入AD域的方法步骤

《WindowServer2016加入AD域的方法步骤》:本文主要介绍WindowServer2016加入AD域的方法步骤,包括配置DNS、检测ping通、更改计算机域、输入账号密码、重启服务... 目录一、 准备条件二、配置ServerB加入ServerA的AD域(test.ly)三、查看加入AD域后的变