Spring-AOP

92 阅读4分钟

概念

AOP : 面向切面编程。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的 耦合度 降低,提高程序的可重用性,同时提高了开发的效率。

底层原理

动态代理

AOP 术语

  1. 连接点

类里面的哪些方法可以被增强,这些方法就叫做 连接点。

  1. 切入点

实际被真正增强的方法,称为 切入点。

  1. 通知(增强)
  • 实际增强的逻辑部分称为 通知(增强)。
  • 通知有多种类型:
    • 前置通知
    • 后置通知 (有异常这个不会执行的)
    • 环绕通知
    • 异常通知
    • 最终通知
  1. 切面

指的是 动作,把通知应用到切入点的过程。

Spring 基于 AspectJ 框架实现 AOP 操作。

SpringAop同类调用会触发拦截吗?为什么

image.png

首先我们看一下Spring Aop的原理图 在Spring中经常使用自定义注解或是spring已经封装 好的注解,通过AOP的方式是实现代码复用,避免重复劳动。而Spring实现AOP是通过动态代理来实现的(默认有接口的情况下使用JDK的动态代理,也可以通过配置proxyTargetClass来制定使用CGLib,没有接口的情况下使用CGLib). 但是无论哪一种代理,都是在原有方法的外面包一层,通过方法外的代理层来实现AOP的逻辑。 也就是说我们首先调用的是AOP代理对象而不是目标对象。但是我们使用this.getInstalledApk()时,this表示的是当前对象,而不是代理对象,因此注解失效。 所以,解决代理无效的根本方法就是获取到代理对象,使用代理对象进行调用。

总结: (1).在一个类内部调用时,被调用方法的 AOP 声明将不起作用。Spring 事务管理注解 @Transactional 也一样。

(2).对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法都必然是 public 的,这就要求实现类的实现方法也必须是 public 的(不能是 protected、private 等),同时不能使用 static 的修饰符。所以,可以实施接口动态代理的方法只能是使用 public 或 public final 修饰符的方法,其他方法不可能被动态代理,相应的也就不能实施 AOP 增强,换句话说,即不能进行 Spring 事务增强了。

(3).基于 CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的。由于使用 final、static、private 修饰符的方法都不能被子类覆盖,相应的,这些方法将无法实施 AOP 增强。所以方法签名必须特别注意这些修饰符的使用,以免使方法不小心成为事务管理的漏网之鱼。

(4).该例中的方法符合上述条件,但注解仍然失效,主要原因是在于同一类中的方法互相调用,调用者指向当前对象,所以无论是接口代理还是 cglib 代理都无法织入增强实现。

原文

AOP的具体应用

  1. 创建 被增强的类 和 被增强的方法
@Component

public class User {

    // 被增强的方法

    public void add() {

    System.out.println("add.......");

    }

}
  1. 创建 增强类 和 增强方法
//增强的类
@Component
@Aspect	//	生成代理对象
public class UserProxy {
    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
    public void before() {
        System.out.println("before.........");
    }
    //后置通知(返回通知):有异常不会执行这个的
    @AfterReturning(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
        public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    //最终通知:不管有没有异常 都会执行
    @After(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
        public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    //环绕通知
    @Around(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws 
        Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

优化——相同的切入点做一个提取

//相同切入点抽取
@Pointcut(value = "execution(* com.snow.spring5.aopanno.User.add(..))")
public void pointdemo() {
}

//前置通知
@Before(value = "pointdemo()")
public void before() {
    System.out.println("before.........");
}

//最终通知
@After(value = "pointdemo()")
public void before() {
    System.out.println("after.........");
}

有多个增强类多同一个方法进行增强,设置增强类优先级

(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component 
@Aspect 
@Order(1) 
public class PersonProxy