1、AOP 使用
/**
* FileName:MainConfigOfAOP
* @author Sam
* @date 2021-06-13-23:20
* Description: AOP 测试
*
* 1、导入aop 模块: Spring AOP ;(spring-aspects)
* 2、定义一个业务逻辑类:在业务逻辑前、后、运行时打印
* 3、定义一个日志切面类(LogAspects) : 切面类里面的方法需要动态感知 业务逻辑类 放发运行到的存在
* 通知方法:
* 前置通知(@Before):在目标方法运行之前运行
* 后置通知(@After):在目标方法运行结束之后运行,无论方法是正常返回还是异常返回。
* 返回通知(@AfterReturning):在目标方法正常返回之后运行
* 异常通知(@AfterThrowing):在目标方法出现异常以后运行
* 环绕通知(@Around):动态代理,手动推进目标方法运行
* 4、给切面类的目标方法标注何时何地运行(通知注解)
* 5、将切面类和业务逻辑类都加入到容器中
* 6、告诉Spring那个类是切面类(给切面类上加一个注解:@Aspect)
* 7、给配置类中加 @EnableAspectJAutoProxy 开启基于注解的AOP模式
*/
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
LogAspects logAspects() {
return new LogAspects();
}
}
/**
* FileName:MathCalculator
*
* @author Sam
* @date 2021-06-13-23:28
* Description: AOP业务逻辑类
*/
public class MathCalculator {
public void div(int i, int j) {
int a = i/j;
System.out.println("MathCalculator 。。div = " + a);
}
}
/**
* FileName:LogAspects
*
* @author Sam
* @date 2021-06-13-23:30
* Description: 切面类
*/
@Aspect
public class LogAspects {
/**
* 抽取公共的切入点表达式
*
*/
@Pointcut("execution(public void com.hupun.aop.MathCalculator.*(..))")
public void pointCut(){};
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println(joinPoint.getSignature() + "除法开始。。。。@Before" + " 参数" + Arrays.toString(args));
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println("方法名"+ joinPoint.getSignature().getName()+"除法结束。。。。@After");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("除法正常返回。。@AfterReturning: 运行结果:" + result);
}
@AfterThrowing(value = "pointCut()", throwing = "e")
public void logException(Exception e) {
System.out.println("除法异常。。。异常信息 " + e);
}
}
// 使用的时候 必须要从 IOC 容器中获取,不然切面是不生效的,因为用的是spring的注解,所以不用spring管理的对象,是不可以的
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
MathCalculator mathCalculator = (MathCalculator) SpringApplication.run(DemoApplication.class, args).getBean("mathCalculator");
mathCalculator.div(1, 0);
}
}
2、AOP原理
AOP 原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
@EnableAspectJAutoProxy
1、@EnableAspectJAutoProxy 是什么?
@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar.class
利用AspectJAutoProxyRegistrar自定义给容器中注册bean。
internalAutoProxyCreator = AnnotationAwareAspectAutoProxyCreate
图后面补
继承关系
2、AnnotationAwareAspectJAutoProxyCreator
-》AspectJAwareAdvisorAutoProxyCreator
-》abstractAdvisorAutoProxyCreator
-》abstractAutoProxyCreator
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
准备调试,以下类打上端点:
每个实现类中主要用到的方法都打上端点:
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.postProcessBeforeInstantiation()
AbstractAutoProxyCreator.postProcessAfterInitialization()
AbstractAdvisorAutoProxyCreator.setBeanFactory() -> initBeanFactory()
-》其中initBeanFactory()方法使用了模板模式
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
流程:(每一步的图后面补)
1)、传入配置类,创建一个新的IOC(ApplicationContext)容器。SpringApplication.run()
2)、注册配置类,刷新容器 refresh()
3)、registerBeanPostProcessors(beanFactory):注册bean的后置处理器来方便拦截bean
1、先从IOC容器中获取到已经定义了的需要创建对象的所有BeanPostProcessor
2、给容器中加入别的BeanPostProcessor
3、优先注册实现Ordered接口的BeanPostProcessor
4、再给容器中注册实现了Ordered接口的BeanPostProcessor
5、注册没实现接口优先级接口的BeanPostProcessor
6、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中。
创建internalAutoProxyCreator 的 BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
1)、创建Bean的实例
2)、populateBean(beanName, mbd, instanceWrapper); 给bean的各种属性赋值
3)、initializeBean(beanName, exposedObject, mbd); 初始化bean
1)、invokeAwareMethods(beanName, bean); 处理Aware接口的方法回调
2)、applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName):应用bean的后置处理器 BeforeInitialization通知。
3)、invokeInitMethods(beanName, wrappedBean, mbd); 执行自定义初始化方法
4)、applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);应用Bean的后置处理器的 AfterInitialization通知。
4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功
7、把BeanPostProcessor注册到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
===========================以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程=========================
AnnotationAwareAspectJAutoProxyCreator =》 InstantiationAwareBeanPostProcessor
4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作
1、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean-->doGetBean()-->getSingleton()
2、创建bean
【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截, InstantiationAwareBeanPostProcessor, 会调用postProcessorBeforeInstantiation()】
1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建。把创建好的bean都会缓 存起来。
2)、createBean();创建bean,AnnotationAwareAspectJAutoProxyCreator会再任何bean创建之前先尝试返回bean 实例。
【BeanPostProcessor 是在bean对象创建完初始化前后调用的】
【InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象的】
1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation 希望后置处理器在 此返回一个代理对象,如果能返回代理对象就使用,如果不能就继续。
1)、后置处理器先尝试返回对象;
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor就执行 postProcessBeforeInstantiation。
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
2)、doCreateBean(beanName,mbdTouse.args);真正去创建一个bean
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator 和 LogAspect的创建
1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
2)、判断当前bean是否是基础类型的Advice、PointCut、Advisor、AopInfrastructureBeforeXX 或者是否是切面 (@Aspect)
3)、是否需要跳出
1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
每一个封装的通知方法的增强器是InstantiationModelAwarePointCutAdvisor;
判断每一个增强器是否是AspectJPointcutAdvisor类型的,返回true。
2)、永远返回false。 因为我们的是InstantiationModelAwarePointCutAdvisor
2)、创建对象
postProcessAfterInitialization:
return wrapIfNecessary(bean, beanName, cacheKey); // 包装如果需要的情况下
1)、获取当前bean的所有增强器(通知方法)Object[] specificInterceptors
1、找到候选的所有的增强器(找那些通知方法是需要切入当前的bea方法的)
2、获取到能在bean使用的增强器
3、给增强器排序
2)、保存当前bean在advisedBeans中;
3)、如果当前bean需要增强,创建当前bean的代理对象
1)、获取所有增强器(通知方法)
2)、保存到ProxyFactory
3)、创建代理对象:Spring自行决定
JdkDynamicAopProxy(config); jdk 动态代理
ObjenesisCglibAopProxy(config);cglib 动态代理
4)、给容器中返回当前组件使用cglib 增强的代理对象;
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程。
3)、目标方法执行:
容器中保存了组件的代理对象(cglib增强后的对象),这个对象保存了详细信息(比如增强器,目标对象,XXX);
1)、CglibAOPProxy.intercept(); 拦截目标方法的执行
2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice()
1)、List<Object> interceptionList保存所有拦截器5
一个默认的ExposeInvocationInterceptor 和 4个增强器。
2)、遍历所有的增强器,将其转为Interceptor
registry.genInterceptors(advisor);
3)、将增强器转为List<MethodInterceptor>;
如果是MethodInterceptor 将增强器转为MethodInterceptor;
转换完成返回MethodInterceptor数组。
3)、如果没有拦截器链,直接执行目标方法。
4)、如果有连接器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个CglibMethodInvocation对象。
并调用retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
5)、拦截器链的触发过程;
1)、如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法。
2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行,
拦截器链的机制,保证通知方法与目标方法的执行顺序。
3、总结
1)、@EnableAspectJAutoProxy 开启AOP功能。
2)、@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
3)、AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器;
4)、容器的创建流程:
1)、registerBeanPostProcessor()注册后置处理器,创建AnnotationAwareAspectJAutoProxyCreator
2)、finishBeanFactoryInitialization() 初始化剩下的单实例bean
1)、创建业务逻辑组件和切面组件
2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
3)、组件创建完之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor),给业务逻辑组件创建一个代理对象
5)、执行目标方法:
1)、代理对象执行目标方法
2)、CglibAopProxy.intercept()
1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
3)、效果:
正常执行: 前置通知--> 目标方法 --> 后置通知 --> 返回通知
出现异常: 前置通知--> 目标方法 --> 后置通知 --> 异常通知
递归+ 责任链模式: