AOP:面向切面编程,是对oop(面向对象)的一种补充,完成面向对象完成不了的一小类问题,如:日志管理,性能检查,事物等等;(建议真正感兴趣的读者,结合底层源码,可以去看一下网名为:墨家巨子@俏如来 的文章,我这一篇相当于一个自己的笔记,加总结)
下面我们直接来了解Spring的AOP的实现原理
1.我们知道springAOP分为注解形式,和xml形式;但是无论是注解形式,还是xml形式,我们都需要在配置文件中添加对应的"自定义标签":
如注解形式:
当然,除了AOP的自定义标签,还有其它的自定义标签,比如事物.....等,spring为了实现这些特殊且标签的解析,和其功能,spring特意提供了一个公共解析规则接口,来统一管理spring自定义标签的解析NamespaceHandlerSupport
我们可以看到NamespaceHandlerSupport接口下面实现了很多实现类,其中就包括我们熟悉的Aop自定义标签解析的类和事物标签的解析类;为什么spring对自定义标签,还额外分别生成对应的解析类和公共接口规则,我个人认为是为了实现"职责分裂",方便管理和后期的维护;
当我们认识到spring会对自定义标签,生成其对应的自定义解析器后,接下来我们看源码
2. AopNamespaceHandler类
进入AopNamespaceHandler类,在这里我们可以看到spring针对于不同形式的AOP注册了不同的解析类:
ConfigBeanDefinitionParser:基于xml的Aop解析类,
AspectJAutoProxyBeanDefinitionParser:基于注解形式的Aop解析类;(本文我们主要看基于注解的Aop)
3.进入AspectJAutoProxyBeanDefinitionParser类
该方法的作用是创建一个AspectJAnnotionAutoProxyCreator自动代理创建器:它的作用就是用来处理AOP
4.进入AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
在这里spring注册了两个功能,
1.注册基于AOP的注解解析器:AnnotationAutoProxyCreator
2.对aop标签的两个属性赋值
2.1 proxy_target_class:指定当前AOP代理使用那种代理模式(jdk、cglib)
2.2 expose_proxy
5.继续进入AopConfigUtils.registerOrEscalateApcAsRequired()方法
在 registerOrEscalateApcAsRequired()方法中,总共就做了一件事情,生成Aop解析器,先从缓存中获取,如果缓存中有,则根据优先级选择使用那个,如果不存在,则新生成一个注解解析器,并加入缓存中
6.现在注解解析器有了,那么我们接下来真正看Aop是怎么实现的,Aop的功能实现,跟bean的加载流程息息相关
springAop的实现是在初始化后,在beanPostProcessor的后置处理器中实现的,我们直接看初始化后的代码
7.进入初始化方法:initializeBean
进入初始化方法后,我们可以看到,在这里spring调用了beanPostProcessor的后置处理器
8.进入beanPostProcessor的后置处理器的调用
在这里就做了一件事情,调用当前spring中的所有的beanPostProcessor的后置处理器方法
9.接下来我们直接看基于Aop的注解解析器:AbstractAutoProxyCreator(beanPostProcessor的子类)
10. 进入wrapIfNecessary()方法
在这里首先判断当前bean是否需要代理增强,如果不需要,则直接返回,如果需要,则寻找bean的增强器,然后根据增强器创建代理对象
11.寻找bean的增强器getAdvicesAndAdvisorsForBean()方法
寻找bean的增强器,判断符合当前bean的增强器,并封装成List<advisor>返回
12.寻找所有的bean的增强器findCandidateAdvisors()方法
13.我们继续看寻找AOP的增强器方法buildAspectJAdvisors()
在这里真正的开始进行寻找切面类,获取增强方法,这里其实是先判断缓存中是否有增强方法,如果有则直接走下面的从缓存中获取增强器,如果没有,则在查询当前项目中的增强器,并缓存起来
14.进入this.advisorFactory.getAdvisors()方法:寻找增强器
15.进入getAdvisor()方法
getPointcut()方法
findAspectJAnnotationOnMethod()方法:寻找切点信息
找到切点信息,并封装成AspectJExpressionPointcut返回
进入InstantiationModelAwarePointcutAdvisorImpl()构造器方法
16.进入instantiateAdvice()方法
找到增强器中的,增强方法,并将其封装到对应的Advice类中;在这里我们就找到了所有的增强器了,接下来,就用这些增强器创建,代理增强对象
17.createProxy()方法:创建代理对象
18.proxyFactory.getProxy(this.proxyClassLoader):方法:创建代理对象
在这里,我们可以看到springAop是通过代理模式,来实现功能增强,来实现的Aop;假如目标类是实现了接口的类,且proxy_target_class为false则默认使用jdk动态代理,假如proxy_target_class为true,无论改类有没有实现接口,则使用cglib动态代理,加入该类没有实现接口,则直接使用cglib动态代理
19.创建完代理对象后,接下来执行目标方法
首先获取增强器执行链,就是对应的增强通知方法
20.进入proceed()方法:这个方法会涉及到一个复杂的调用链
第一次进来,this.currentInterceptorIndex=-1,
this.interceptorsAndDynamicMethodMatchers.size() - 1 = 4;此时-1 != 4,则不执行目标方法,则走下面的执行链的增强方法
走到第一个增强器方法后,在执行过程中又回到了,proceed方法,然后在接着看proceed方法
第二次进入proceed()方法
此时this.currentInterceptorIndex=-1,
this.interceptorsAndDynamicMethodMatchers.size() - 1 = 3;此时-1 != 3,则不执行目标方法,则继续走下面的执行链的增强方法
在这里,我们看到第二次,走增强方法的时候,走到返回通知的方法,在Aop里面,返回通知里面的增强方法,是不管目标方法是否报错,都会执行的方法,在这里,我们可以看到返回通知里的方法确实也是这样,不管mi.proceed()主干流程方法是否会报错,到最后,都会执行finally中的方法,然后继续回到proceed()方法(注意,在这几次走增强方法时,每个增强方法都没执行完,然后就继续调用了proceed方法,这就是springAop执行增强方法的巧妙之处,感兴趣的读者可以下来自行了解)
第三次进入proceed()方法
此时this.currentInterceptorIndex=-1,
this.interceptorsAndDynamicMethodMatchers.size() - 1 = 2;此时-1 != 2,则不执行目标方法,则继续走下面的执行链的增强方法
此时走到了异常通知,异常通知:当目标方法发生报错时,才会执行异常通知,在这里,我们可以看到,同样的首先,继续调用proceed()方法,然后通过catch(){}包住了,异常通知增强方法,只有在程序发生错误时,才会走异常通知的增强方法
继续走proceed()方法
第四次次进入proceed()方法
此时this.currentInterceptorIndex=-1,
this.interceptorsAndDynamicMethodMatchers.size() - 1 = 1;此时-1 != 1,则不执行目标方法,则继续走下面的执行链的增强方法
此时走到了后置通知,后置通知:程序一切正常,调用目标方法之后,走后置通知,如果目标方法发生报错,则不走后置通知,在后置通知invoke()方法中,也是非常符合这个逻辑的,这里我们可以看到,进来之后首先继续调用执行链,如果执行链没有报错,则在走后置通知的增强方法,在这之前,我们已经走了2个增强了,分别是返回通知和异常通知,还剩下前置通知,和执行目标方法了,根据执行链,越调用后面的通知,越早执行,后置通知,是当前置+目标方法正常执行,不报错,则在执行后置通知,所以这里非常符合,在这里首先继续调用执行链,就是调用前置通知+目标放,然后调用完成之后,如果两个都没报错,则走这里的后置通知增强方法,否则直接抛异常,往上执行,就不在走this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());方法了
接着,我们继续往走执行链
此时第五次次进入proceed()方法
此时this.currentInterceptorIndex=-1,
this.interceptorsAndDynamicMethodMatchers.size() - 1 = 0;此时-1 != 0,则不执行目标方法,则继续走下面的执行链的增强方法
此时走到前置通知,在这里我们可以看到,前置通知里面的invoke()方法,跟之前的通知都不一样了,在这里是先走的前置增强,然后在走的执行链,为什么呢,我们都知道前置通知,是在目标方法执行前调用,既然我们现在都已经走到了前置通知方法了,那么下一步,毋庸置疑,肯定就是执行目标方法了,既然下一步肯定是执行目标方法了,所以在前置通知这里,就可以完全先执行前置增强方法,然后在执行执行链,因为到执行链的时候,就执行目标方法了
继续往下执行,走执行链
在次进入到proceed()方法后,此时-1=-1了,则就执行目标方法,不在走下面的增强器了,执行完目标方法后,在一层一层的返回执行之前在各个增强器里面,没有执行完的增强方法;AOP整体流程也就到这里了