Spring AOP之@AspectJ注解

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

Spring AOP之@AspectJ注解

Spring2.0之后发布的新的特性:

  • @AspectJ注解到POJO来定义Aspect以及Advice。
  • xml配置的简化,引入aop独有的命名空间方式,来配置aop。

Spring会搜索注解了@AspectJ的类,然后将他们织入系统中。

使用

定义一个Aspect如下:

@Aspect
public class PerformanceTraceAspect {
    private final Log logger = LogFactory.getLog(PerformanceTraceAspect.class);

    @Pointcut("execution(public void *.method1()) || execution(public void *.method2())")
    public void pt() {}

    @Around("pt()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch watch = new StopWatch();
        try {
            watch.start();
            return joinPoint.proceed();
        } finally {
            watch.stop();
            if (logger.isInfoEnabled()) {
                logger.info("PT in method[" + joinPoint.getSignature().getName() + "]>>>" + watch.toString());
            }
        }

    }
}
复制代码

如何将这个Aspect织入到我们的目标对象中?有2种方式:

编程织入方式

使用AspectJProxyFactory:

AspectJProxyFactory weaver = new AspectJProxyFactory();
weaver.setProxyTargetClass(true);
weaver.setTarget(new Foo());
weaver.addAspect(PerformanceTraceAspect.class); //将切面添加进去
Object proxy = weaver.getProxy();
复制代码
自动代理织入方式

使用AutoProxyCreator:AnnotationAwareAspectJAutoProxyCreator。它会自动收集IOC容器中注册的Aspect,并作用到目标对象上。

只需要在配置文件中注入AnnotationAwareAspectJAutoProxyCreator,spring2.x之后提供了更简单的配置:

<aop:aspectj-autoproxy proxy-target-clas="true">
</aop:aspectj-autoproxy>
复制代码

@AspectJ形式的Pointcut

@Pointcut("execution(public void *.method1()) || execution(public void *.method2())")
public void pt() {}
复制代码

SpringAOP只集成了AspectJ的Pointcut的部分功能,其中包括Pointcut的语言支持。

@Aspect的pointcut申明方式主要包含2个部分:

  • Pointcut Expression
    • 真正指定Pointcut的地方。
    • 表达式中可以&& || !这些逻辑运算符
  • Pointcut Signature
    • 需要一个定义的方法做为载体,这个方法必须是void类型
    • 如果该方法是public的,那么这个pointcut可以被其他的Aspect引用,如果是private那么只能被当前Aspect类引用。

Aspectj的pointcut表述语言中有很多标志符,但是SpringAOP只能是用少数的几种,因为Spring只对方法级别的pointcut。

  • execution
    • 规定格式:execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
    • 只有返回类型,方法名,参数模式是必须的,其他的可以省略。
    • 这里面我们可以使用2种通配符
      • * 匹配任意的意思
      • ..当前包以及子包里面所有的类
  • within
    • 只接受类型的声明,会匹配指定类型下面所有的Jointpoint。对SpringAOP来说及,匹配这个类下面所有的方法。
  • this和target
    • this指代方法调用方,target指被调用方。
    • this(o1) && this(o2) 即表示当o1类型对象,调用o2类型对象的方法的时候,才会匹配。
  • args
    • 指定参数的类型,当调用方法的参数类型匹配就会捕捉到。
  • @within
    • 指定某些注解,如果某些类上面有指定的注解,那么这个类里面所有的方法都将被匹配。
  • @target
    • 目标类是指定注解的时候,就会被匹配,SpringAOP中和@within没什么区别,只不过@within是静态匹配,@target是运行时动态匹配。
  • @args
    • 如果传入的参数的类型 有其指定的注解类型,那么就被匹配。
  • @annotation
    • 系统中所有的对象的类方法中,有注解了指定注解的方法,都会被匹配。

这些注解的pointcut在spring内部最终都会转为具体的pointcut对象。

@AspectJ形式的Advice

主要就是一些Advice的注解:

  • @Before
    • 想要获取方法的参数等信息:可以2种方法
      • 第一个参数设置为JoinPoint,这个参数必须要放在第一个位置,并且除了Around Advice和Introduction不可以用它,其他的Advice都可以使用。
      • args标志符绑定(不常用)
  • @AfterReturning
    • 有一个独特属性:returning,可以获取到方法返回值。
  • @AfterThrowing
    • 有一个独特属性:throwing 可以接受抛出的异常对象。
  • @After(也叫finally)
    • 一般做资源的释放工作的
  • @Around
    • 它的第一个参数必须是ProceedingJoinPoint类型,且必须指定。通过ProceedingJoinPoint的proceed()方法执行原方法的调用。
    • proceed()方法需要传入参数,则传入一个Object[]数组。
  • @DeclareParents
    • 处理Introduction的,不多描述了。

其他

advice的执行顺序
  • 如果这些advice都在一个aspect类里面:

相同的advice按照申明顺序做优先级,但是注意一点,BeforeAdvice先申明优先级高,则先执行。而AfterReturningAdvice则是先申明优先级高,但是优先级高的越后执行。

before1
before2
task
after returning 2
after returning 1
复制代码
  • 如果这些Advice在不同的Aspect里面:

借助于Ordered接口,否则Advice的执行顺序是无法确定的。

Aspect的实例化模式

有3种:singleton,perthis,pertarget。(不太重要)

猜你喜欢

转载自juejin.im/post/7035648108486721544