前言
一、什么是AOP
二、AOP重要的概念
切点: 切点就是具体需要去
切
的地方(具体需要增强的方法)通知: 通知包含两要素:①通知的时间;②通知的信息(具体要做什么事情)
切面: = 切点 + 通知(去哪里?什么时候?去做些什么?)
连接点: 连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出以异常时、甚至修改一个字段时
目标对象: 需要被代理的类,如IndexService
织入: 把一个切面应用到真实对象上面的过程,就叫做织入(知道时间、地点、任务后,最后就是执行啦)
前置通知(Before Advice): 方法执行前执行
后置返回通知(After Return Advice): 方法返回之前执行,可以对方法的返回值进行修改,在此之前如果出现异常则不会执行
后置通知(After Advice): 方法返回之后执行,不可以对方法的返回值进行修改,即使出现异常也会执行
异常通知(After Throwing Advice): 方法执行异常后执行
环绕通知(Around Advice): 环绕通知可以看成是前四个通知的集合,通过它就可以完成前四个通知的任务,并且环绕通知会比前置通知先执行。
三、.写一个小Demo
ApplicationConfig
@EnableAspectJAutoProxy
@Configuration
public class ApplicationConfig {}
LogAspect
package com.example.demo.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* <p>日志AOP配置类</p>
* @author Bosen
* @date 2021/9/17 15:37
*/
@Component
@Aspect
public class LogAspect {
/**
* <p>配置切面</p>
*/
@Pointcut("execution(public * com.example.demo.service.*.*(..))")
public void logPointcut(){}
/**
* <p>前置通知</p>
*/
@Before("logPointcut()")
public void logBefore() {
System.out.println("This is LogAspect before");
}
/**
* <p>后置返回通知</p>
*/
@AfterReturning("logPointcut()")
public void logAfterReturning() {
System.out.println("This is LogAspect AfterReturning");
}
/**
* <p>异常通知</p>
*/
@AfterThrowing("logPointcut()")
public void logAfterThrowing() {
System.out.println("This is LogAspect AfterThrowing");
}
/**
* <p>后置通知</p>
*/
@After("logPointcut()")
public void logAfter() {
System.out.println("This is LogAspect After");
}
}
IndexService
public class IndexService {
public void echo() {
System.out.println("This is IndexService echo");
}
}
调用echo方法
@SpringBootTest
class DemoApplicationTests {
@Autowired
IndexService service;
@Test
void contextLoads() {
service.echo();
}
}
环绕通知Around Advice
@Component
@Aspect
public class LogAspect {
/**
* <p>配置切面</p>
*/
@Pointcut("execution(public * com.example.demo.service.*.*(..))")
public void logPointcut(){}
/**
* <p>环绕通知</p>
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) {
// 得到方法执行所需的参数
Object[] args = joinPoint.getArgs();
Object proceed = null;
try {
System.out.println("前置通知Before");
// 明确调用业务层方法
proceed = joinPoint.proceed(args);
System.out.println("后置返回通知AfterReturning");
}catch (Throwable throwable){
System.out.println("异常通知AfterThrowing");
throwable.printStackTrace();
} finally {
System.out.println("后置通知After");
}
return proceed;
}
}
四、Spring AOP执行流程
@EnableAspectJAutoProxy
开启aop底层是将
AspectJAutoProxyRegistrar
注入spring容器AspectJAutoProxyRegistrar通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)的底层
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source)
将AnnotationAwareAspectJAutoProxyCreator
注入spring容器AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor的子类,也是AOP的入口
AnnotationAwareAspectJAutoProxyCreator的上级中
AbstractAutoProxyCreator
为AOP的核心类AbstractAutoProxyCreator重新了后置处理方法
postProcessAfterInitialization
,其中通过wrapIfNecessary
创建代理Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));层层包装调用,进入到
DefaultAopProxyFactory
的createAopProxy
方法创建代理创建代理时判断目标对象是否实现接口,是则使用
JDK
的动态代理,否则使用CGLIB
的动态代理(CGLIB可以适用于有接口或无接口的类,但JDK动态代理只可以用于实现了接口的类)在JDK的动态代理的类JdkDynamicAopProxy中,可看到其中invoke方法实现代理功能
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 获取aop的所有通知
使用责任链模式执行所有通知,执行目标方法
当我们在调用
getBean
去实例化springbean时,安装springbean的生命周期,在执行完bean自定义的init方法后,进入后置处理
这时就会创建代理类,当bean执行方法时,进入代理类的invoke,执行所有通知,再执行目标方法,实现代理功能







