AOP 简单使用:
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式:
1.定义一个切面类,切面类里面的方法需要感知方法运行到哪一步,来执行相应的通知:
- @Before: 前置通知,在目标方法运行之前运行,logStart
- @After:后置通知,在目标方法运行之后运行,logEnd
- @AfterReturning:返回通知,在目标方法正常返回之后运行,logReturn
- @AfterThrowing:异常通知,在目标方法发生异常以后运行,logException
- @Around:环绕通知,动态代理,手动推进目标方法执行
2.给切面类的目标方法标注何时何地运行(在切面类的方法上标注通知注解)
3.将切面类和业务逻辑类加到容器中
4.给切面类加@Aspect注解,将之声明称一个切面
5.给配置类加上@EnableAspectJAutoProxy,开启基于注解的aop模式。
@Aspect
@component
public class LogAspects{
@pointcut("execution(public void com.aop.Math.div(int, int))")
public void pointCut();
@Before("pointCut()")
public void logStart(){
System.out.println("开始。。。");
}
@After("pointCut()")
public void logEnd(){
System.out.println("结束。。。");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("正常返回。。。");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("出现异常。。。");
}
}
6.获取返回值和异常信息:
@AfterReturning(value="pointCut()", returning="result")
public void logReturn(Object result){
System.out.println("正常返回。。。,返回值是:"+result);
}
@AfterThrowing(value="pointCut()", throwing="exception")
public void logReturn(Exception exception){
System.out.println("出现异常。。。,异常是:"+exception);
}
AOP 原理:
(看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么):
@EnableAspectJAutoProxy:
给容器中注册了一个AnnotationAwareAspectJAutoProxyCreator
流程:
-
1.传入配置类,创建IOC容器
-
2.注册配置类,调用refresh() 刷新容器
-
3.registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来拦截bean的创建
- 1.先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor.
- 2.给容器中加别的BeanPostProcessor
- 3.优先注册实现了PriorityOrdered接口的BeanPostProcessor
- 4.在给容器中注册实现了Ordered接口的BeanPostProcessor
- 5.注册没实现优先级接口的BeanPostProcessor
- 6.注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中。
- 7.把BeanPostProcessor注册到BeanFactory中
至此就完成了AnnotationAwareAspectJAutoProxyCreator 的创建和注册
-
4.finishBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化操作,创建剩下的单实例bean
-
5.每一个bean创建之前,调用postProcessBeforeInstantiation();
主要关心目标类 和切面类的创建- 1):判断当前bean是否在advisedBeans中,(保存了所有需要增强的bean)
- 2):判断当前Bean是否是基础类型的Advice,Pointcut,Advisor,AopInfrastructureBean,或者是否是用@Aspect注解标注的切面。
- 3):是否需要跳过,
- 1):获取候选的增强器(切面里面的通知方法)【List candidateAdvisors】每一个封装的通知方法的增强器是InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是AspectJPointcutAdvisor类型的,返回true
- 2)永远返回false
-
6.调用postProcessAfterInstantiation(); return wrapIfNecessary(bean,beanName,cacheKey);//包装如果需要的情况下
- 1):获取当前bean的所有增强器(通知方法)Object[]
- 1.找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
- 2.找到能在当前bean使用的增强器
- 3.给增强器排序
- 2):保存当前bean在advisedBeans中;
- 3):如果当前bean需要增强,创建当前bean的代理对象;
- 1):获取所有增强器(通知方法)
- 2):保存到proxyFactory
- 3):创建代理对象,Sprign自动决定
JdkDynamicaAopProxy(config);jdk的动态代理
ObjenesisCglibAopProxy(config);cglib的动态代理 - 4):给容器中返回当前组件使用cglib增强了的代理对象
- 5):以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程
- 1):获取当前bean的所有增强器(通知方法)Object[]
-
7.目标方法执行:
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx)- 1),CglibAopProxy.intercept();拦截目标方法执行
- 2),根据ProxyFactory对象获取将要执行的目标方法的拦截器链
- 3),如果没有拦截器链,直接执行目标方法
- 4)如果有拦截器链:把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个CglibbMethodInvocation对象,并调用其proceed()方法
- 5),拦截器链的触发过程:
- 1),如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
- 2),链式获取每一个拦截器,执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。拦截器链的机制,保证通知方法与目标方法的执行顺序
总结:
- 1):@EnableAspectJAutoProxy 开启AOP功能
- 2)@EnableAspectJAutoProxy注解会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
- 3)AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器
- 4)容器的创建流程:
- 1)registerBeanPostProcessors() 这个方法注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator
- 2)finishBeanFactoryInitialization()方法初始化剩下的单实例bean
- 1)创建业务逻辑组件和切面组件
- 2)AnnotationAwareAspectJAutoProxyCreator拦截组件的创建
- 3)组件创建完成之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象
- 5)执行目标方法
- 1)代理对象执行目标方法
- 2)CglibAopProxy.intercept();
- 1)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
- 2)利用拦截器的链式机制,依次进入每一个拦截器进行执行
题后话,由于作者水平有限,文章中难免会有歧义的地方,欢迎大家批评指正,作者定会及时改正