AOP与IOC的衔接
接着之前IOC的Bean的拓展,我们再联合AOP一起去考虑。那么AOP其实需要将之前的Bean进行代理,所以需要选择合适的切入点整合IOC的一整套流程。所以应该是在初始化之后的处理器BeanPostProcessor#postProcessAfterInitialization使用代理进行接入替换。而首先我们会有EnableAspectJAutoProxy注解进行将该后置处理器进行注册,当然我们这里进行代理之后使用的引用也会改变,所以这里会针对早起的引用会有一个map进行存储使用SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference进行拓展,供后面使用aop接入。其中我们的后置处理器是在EnableAspectJAutoProxy注解,通过AspectJAutoProxyRegistrar引入AnnotationAwareAspectJAutoProxyCreator进行拓展。
AOP的概念
首先我们先对aop的概念有一个了解,而后我们将这些概念进行建模设置对应的接口,同时将这些接口进行组合,最终去实现我们需要的面向切面的功能。
- Join Point(连接点):程序中需要进行切面的程序执行过程的点。比如方法调用,异常处理,Spring AOP支持方法级别的连接点。
- Pointcut(切入点):识别程序连接点的模式归纳。
- Adice(通知):需要切入之后执行的方法,分Before Adice、After Adice、After Returning Adice、After Throwing Adice、Around Adice
- Aspect(切面):切入点和通知的结合
- Target Object:目标对象
- AOP proxy:代理对象
- Weaving(织入):将通知通过切入点织入连接点
AOP的实现总概
AOP所要处理的事情就是想要查找一些特定的方法或者程序执行中的一些点,在这些点周围进行执行别的方法,其实也就是织入这个概念。而实现的根本原理上是根据动态代理进行实现。而我们使用JDK的动态代理进行讲解。
AOP的接口
- Join Point:org.aopalliance.intercept.Joinpoint接口,主要是process执行方法
- Pointcut:org.springframework.aop.Pointcut接口,主要是class过滤和方法过滤方法,也就是查找符合切面的连接点的抽象
-
Advice:
- org.aopalliance.aop.Advice接口
- org.springframework.aop.BeforeAdvice接口
- org.springframework.aop.AfterAdvice接口
- org.springframework.aop.AfterReturningAdvice接口
- org.springframework.aop.ThrowsAdvice接口
- AOP Object:org.springframework.aop.framework.AopProxy接口
- Weaving:org.aopalliance.intercept.MethodInvocation接口,其实是Joinpoint接口的加强,同时也需要将通知进行注入过去进行切面的时候进行调用
AOP整体串联
首先我们需要把所有的切面找到,这样我们才有判断条件找到连接点。然后我们需要对每一个Bean根据收集起来的切面的切入点进行判断是否需要代理,所以根据找到接入点将切面的的通知进一步接入代理,完成整个过程。我们将这些过程拆解为以下步骤
- 切面的收集
- 所有切面的切入点的判断
- 找到合适的连接点的目标对象的同时将该切面的通知收集供生成代理对象
- 将收集好的通知和目标对象生成代理对象注入
切面的收集
切面的收集应该在Bean的实例化之前,所以放在实例化前后置处理器InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation。所以我们查看AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation。Spring的AOP处理这里需要做一个判断,对切面这些Bean不需要处理,所以会判断这个Bean的情况。这在这个时候会去获取所有的通知(Advice),同时也会把切入点(Pointcut)进行收集。那么收集所有通知的时候就需要去获取所有的切面。而最后会在BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors。会获取所有的Bean,判断Bean是否有Aspect注解。有Aspect注解的Bean代表切面,同时去获取有通知注解将这些方法转换为Advice
所有切面的切入点的判断
再Bean初始化之后的拓展点进行判断BeanPostProcessor#postProcessAfterInitialization,往后执行获取拦截器getAdvicesAndAdvisorsForBean进行判断是否需要进行代理。获取所有候选的Advisor,其中一些是PointcutAdvisor,存在切入点,会根据里面的条件判断,将match的Advisor返回。
根据符合条件的通知进行生成代理对象
使用ProxyFactory进行生成代理,将满足的通知Advisor添加,然后根据工厂模式构造代理对象。我们以JdkDynamicAopProxy举例。JDK的动态代理是用Proxy#newProxyInstance进行实现的,我们这个时候已经找好了符合这个Bean下面的通知,我们只要根据不同的类型将通知按照顺序执行即可。Spring是使用责任链的方式去执行多个通知的方式。回过头来我们说的织入就是将通知根据切入点切入连接点,而Joinpoint就是目标对象和切面的组合。其中Spring只支持方法级别的代理,所以是MethodInvocation的拓展Joinpoint接口,而MethodInvocation的实现是ReflectiveMethodInvocation。首先我们需要将通知(Advice)适配成MethodInterceptor这样可以使用责任链的方式执行所有通知。最后ReflectiveMethodInvocation结合适配之后的MethodInterceptor形成代理对象需要执行的完整链路。
注入代理对象
返回代理对象进行缓存