文章

Spring AOP核心概念与实现流程

AOP是什么

Spring AOP(面向切面编程)是一种编程范式,它允许你在不修改业务逻辑代码的情况下,对业务逻辑进行横向切入。它通过预定义的方式(切面)来拦截业务逻辑的执行,从而在特定的时机(连接点)插入额外的处理逻辑。

面向切面编程(AOP)通过提供另一种思考程序结构的方式,补充了面向对象编程(OOP)。在 OOP 中,模块化的关键单元是类,而在 AOP 中,模块化的单元是切面。切面使得跨越多个类型和对象的关注点(如事务管理)可以进行模块化。在 AOP 文献中,这类关注点通常被称为“横切”关注点。

Spring AOP 基于动态代理实现(或CGLIB),主要应用于日志记录、权限检查、事务管理等场景。

基本概念

  • 横切关注点:对哪些点进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
  • 连接点:指那些被拦截到的点,可以是方法调用时,抛出异常时,甚至修改字段时。在Spring中,指可以被动态代理拦截目标类的方法。
  • 切入点:指要对哪些Joinpoint进行拦截,即被拦截的连接点。
  • 通知:拦截到Joinpoint之后要做的事,即对切入点的增强内容。
  • 切面:切面就是对横切关注点的抽象。即切入点+通知

具体流程

首先,决定在哪里插入切面,然后,定义切入点,并编写对应方法进行切入处理。

例如,按照最现代的方式处理切面(使用AspectJ并用注解的形式定义切入点):

//声明当前类为Aspect切面,并交给Spring容器管理
@Component
@Aspect
public class LogAnnotationAspectj {
    private final static String EXPRESSION = 
                            "execution(* com.apesource.service.impl.*.create*(..))";
 
    //前置通知   
    @Before(EXPRESSION)
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj前置通知】 ==========");
    }
 
 
    //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知
    @AfterReturning(value = EXPRESSION,returning = "returnVal")
    public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }
 
    //后置通知
    @After(EXPRESSION)
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }
 
    //环绕通知
    @Around(EXPRESSION)
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("##########【环绕通知中的前置通知】##########");
        Object returnVale = joinPoint.proceed();
        System.out.println("##########【环绕通知中的后置通知】##########");
        return returnVale;
    }
 
    // 异常通知:方法出现异常时,执行该通知
    @AfterThrowing(value = EXPRESSION,throwing = "ex")
    public void throwAdvice(JoinPoint joinPoint, Exception ex){
        System.out.println("********** 【Aspectj异常通知】执行开始 **********");
        System.out.println("出现异常:" + ex.getMessage());
        System.out.println("********** 【Aspectj异常通知】执行结束 **********");
    }
 
}

注意到,这里定义切入点的语言并非单纯的正则,而是AspectJ pointcut expression language(AspectJ切入点表达式)。标准的表达式类型是很丰富的,但是Spring Aop只支持其中的9种,外加Spring Aop自己扩充的一种一共是11(10+1)​种类型的表达式,分别如下。

  1. execution:一般用于指定方法的执行,用的最多。
  2. within:指定某些类型的全部方法执行,也可用来指定一个包。
  3. this:Spring Aop是基于动态代理的,生成的bean也是一个代理对象,this就是这个代理对象,当这个对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
  4. target:当被代理的对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
  5. args:当执行的方法的参数是指定类型时生效。
  6. @target:当代理的目标对象上拥有指定的注解时生效。
  7. @args:当执行的方法参数类型上拥有指定的注解时生效。
  8. @within:与@target类似,看官方文档和网上的说法都是@within只需要目标对象的类或者父类上有指定的注解,则@within会生效,而@target则是必须是目标对象的类上有指定的注解。而根据笔者的测试这两者都是只要目标类或父类上有指定的注解即可。
  9. @annotation:当执行的方法上拥有指定的注解时生效。
  10. reference pointcut​:(经常使用)表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持
  11. bean:当调用的方法是指定的bean的方法时生效。(Spring AOP自己扩展支持的)

License:  CC BY 4.0