概念
AOP : 面向切面编程。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的 耦合度 降低,提高程序的可重用性,同时提高了开发的效率。
底层原理
动态代理
AOP 术语
- 连接点
类里面的哪些方法可以被增强,这些方法就叫做 连接点。
- 切入点
实际被真正增强的方法,称为 切入点。
- 通知(增强)
- 实际增强的逻辑部分称为 通知(增强)。
- 通知有多种类型:
- 前置通知
- 后置通知 (有异常这个不会执行的)
- 环绕通知
- 异常通知
- 最终通知
- 切面
指的是 动作,把通知应用到切入点的过程。
Spring 基于 AspectJ 框架实现 AOP 操作。
SpringAop同类调用会触发拦截吗?为什么
首先我们看一下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的具体应用
- 创建 被增强的类 和 被增强的方法
@Component
public class User {
// 被增强的方法
public void add() {
System.out.println("add.......");
}
}
- 创建 增强类 和 增强方法
//增强的类
@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