`

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

阅读更多
摘要 本文介绍使用Spring AOP编程中,在增强处理方法中获取目标方法的参数,定义切点表达式时使用args来快速获取目标方法的参数。

 

获取目标方法的信息

    访问目标方法最简单的做法是定义增强处理方法时,将第一个参数定义为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系列三、四、五》的代码在这里:点击下载,欢迎留言提意见。

    

    【未完,待续】

 

  • 1楼:李少龙 发表于 2014-05-15 15:37 回复此评论
    文章不错,下载链接已失效
     
  • 2楼:摆渡者 发表于 2014-05-15 18:16 回复此评论

    引用来自“李少龙”的评论

    文章不错,下载链接已失效
    分享的链接已经重新开启
     
  • 3楼:李少龙 发表于 2014-05-16 12:33 回复此评论

    引用来自“李少龙”的评论

    文章不错,下载链接已失效

    引用来自“Integer”的评论

    分享的链接已经重新开启
    还是打不开啊。
     
  • 4楼:摆渡者 发表于 2014-05-16 21:09 回复此评论

    引用来自“李少龙”的评论

    文章不错,下载链接已失效

    引用来自“Integer”的评论

    分享的链接已经重新开启

    引用来自“李少龙”的评论

    还是打不开啊。
    微云的分享应该是有时间限制的,我当时能够打开,但第二天就打不开了。我已经将代码上传到github上面,欢迎下载(点击上方的链接即可)
     
  • 5楼:kinnonZhan 发表于 2015-06-06 21:11 回复此评论
    非常不错.之前写过个demo发现不行,然后把楼主的demo下载下来,对比了一下,然后发现自己application.xml缺少下面一句,加上之后完美解决!!!!
    <context:component-scan base-package="com">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" />
    </context:component-scan>
     
  • 6楼:Eurry 发表于 2015-12-24 11:00 回复此评论
    为什么我的增强处理方法 增加了 ProceedingJoinPoint joinPoint 参数后,报404进不去系统呢
     
     
分享到:
评论

相关推荐

    spring-aop-jar

    "spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨Spring AOP、相关jar文件以及它们在实际开发中的作用。 首先,我们来看一下提供的文件: 1. aopalliance.jar:这是一...

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的...无论是 JDK 动态代理还是 CGLIB 动态代理,它们的核心都是在方法调用时插入额外的逻辑,这正是 AOP 的魅力所在。在实际应用中,根据项目需求和性能考虑,可以选择适合的代理方式。

    Spring 入门案例——AOP

    1. **通知(Advice)**:在Spring AOP中,有五种不同类型的通知: - 前置通知(Before Advice):在目标方法被调用之前执行。 - 后置通知(After Advice):在目标方法执行完成后,无论是否抛出异常都会执行。 - ...

    spring-aop.jar

    在Spring AOP中,切面通常包括通知(advice)和切点(pointcut)。通知定义了在特定的切点上执行的行为,而切点则定义了这些行为何时触发。 二、spring-aop.jar组件解析 1. **AOP代理**:Spring AOP支持两种代理...

    spring之AOP(动态代理)

    在运行时,JDK动态代理会创建一个新的类,该类实现目标对象的所有接口,并在方法调用时插入自定义的行为(通知)。Spring的`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口是实现JDK动态...

    Spring——aop

    - **基于注解的AOP**:在Spring 2.5之后引入,无需编写XML配置,直接在方法上使用注解如`@Before`, `@After`, `@Around`, `@AfterThrowing`等定义通知。 ### 3. Spring AOP的核心组件 - **`@Aspect`**:标记一个类...

    Spring3.1AOP简单例子

    在本示例中,我们将深入探讨Spring框架的3.1版本中的核心概念之一:面向切面编程(Aspect-Oriented Programming,简称AOP)。AOP是Spring框架的强大特性,它允许我们在应用程序中实现关注点的分离,使得我们可以将横...

    spring aop依赖jar包

    现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...

    7Spring AOP盗梦空间之二——获得返回值AfterReturnning

    本篇文章将深入探讨Spring AOP中的“AfterReturning”通知类型,即如何在方法正常执行后获取并处理返回值。 在Spring AOP中,我们可以定义切面(Aspect),通过切点(Pointcut)来定位关注点,并使用通知(Advice)...

    springioc和spring aop

    Spring框架是Java开发中不可或缺的一部分,它通过提供两种核心特性——控制反转(IoC)和面向切面编程(AOP)来简化应用的构建。理解并掌握这两种技术对于任何Java开发者来说都至关重要。 **控制反转(IoC)**,也...

    springAOP-dome

    在这个名为"springAOP-dome"的实例中,我们将探讨如何利用Spring AOP实现一个简单的日志记录功能,以作为入门学习。 首先,了解AOP的基本概念是必要的。面向切面编程是一种编程范式,旨在解决程序中的横切关注点,...

    spring-aop

    标题“spring-aop”指的是Spring框架中的一个关键模块——面向切面编程(AOP)。Spring AOP是Spring框架的一部分,它允许开发人员实现横切关注点,如日志、事务管理、性能监控等,这些关注点通常在多个业务逻辑点交叉...

    9Spring AOP 盗梦空间之四——Around

    "9Spring AOP 盗梦空间之四——Around"这个标题暗示我们将深入探讨Spring AOP中的一个关键概念——环绕通知(Around Advice)。环绕通知是Spring AOP中功能最全面的通知类型,它提供了对方法执行前、执行后以及异常...

    spring AOP myeclipse 完整代码

    在MyEclipse中集成Spring AOP,首先需要创建一个Spring项目,并在项目中引入Spring的相关库,包括`spring-aop.jar`和`aspectjweaver.jar`。MyEclipse作为强大的Java集成开发环境,提供了方便的Spring配置工具,可以...

    Spring AOP 类图

    4. **AOP代理(AOP Proxy)**:AOP代理是Spring AOP实现中的一个重要组件,它负责在实际目标对象前后插入通知。Spring提供了两种类型的代理:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的目标对象,而CGLIB...

    T2 spring-aop

    《Spring-AOP实战指南——基于Eclipse环境》 在软件开发中,面向切面编程(Aspect-Oriented Programming,简称AOP)是一种重要的设计模式,它允许开发者将关注点分离,使得业务逻辑与系统横切关注点(如日志、事务...

    Spring aop 性能监控器

    在IT行业中,Spring AOP(面向切面编程)是一种强大的工具,它允许我们在不修改代码的情况下,对应用程序的特定部分进行拦截和增强。这在性能监控、日志记录、事务管理等方面尤为有用。本篇文章将深入探讨如何使用...

Global site tag (gtag.js) - Google Analytics