3.Spring AOP的底层实现技术及源码解析

本文详细探讨了Spring AOP的实现原理,包括切面、连接点、通知、切入点等核心概念的定义,以及Spring如何通过ProxyFactoryBean创建代理对象。讲解了JDK动态代理和CGLIB两种代理方式,分析了拦截器链的调用过程,展示了通知的织入。文章还提供了源码分析,展示了如何通过ProxyFactoryBean生成AOP代理对象,以及拦截器如何通过AOPProxy进行方法拦截。
摘要由CSDN通过智能技术生成

Spring AOP的底层实现技术

三、Spring AOP的实现

我们来试着看看能不能理解SpringAOP底层的实现原理。

3.1 Spring AOP的几个概念

Spring AOP中的几个基本概念,每次学习AOP都被这几个概念折腾的很不爽,我们在这里再把这几个概念描述一遍,力争把这几个概念搞清,在每次review这块内容的时候可以很快上手。

1.切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;

2.连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;

3.通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;

4.切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;

5.目标对象(Target):就是被AOP动态代理的目标对象;

用一张图来形象地表达AOP的概念及其关系如下:

3.2 Spring AOP中切入点、通知、切面的实现

理解了上面的几个概念后,我们分别来看看Spring AOP是如何实现这些概念的;

1.切入点(Pointcut):它定义了哪些连接点需要被织入横切逻辑在Java中,连接点对应哪些类(接口)的方法。因此,我们都能猜到,所谓的切入点,就是定义了匹配哪些娄的哪些方法的一些规则,可以是静态的基于类(方法)名的值匹配,也可以是基于正则表达式的模式匹配。来看看Spring AOP Pointcut相关的类图:

在Pointcut接口的定义中,也许你已经想到了,ClassFilter是类过滤器,它定义了哪些类名需要拦截;典型的两个实现类为TypePatternClassFilter和TrueClassFilter(所有类均匹配);而MethodMatcher为方法匹配器,定义哪些方法需要拦截。

在上面的类图中:

  • StaticMethodMatch与DynamicMethodMatch的区别是后者在运行时会依据方法的参数值进行匹配。
  • NameMatchMethodPointCut根据指定的mappedNames来匹配方法。
  • AbstractRegexpMethodPointCut根据正则表达式来匹配方法。

2.通知(Advice)通知定义了具体的横切逻辑。在Spring中,存在两种类型的Advice,即per-class和per-instance的Advice。

所谓per-class,即该类型的Advice只提供方法拦截,不会为目标对象保存任何状态或者添加新的特性,它也是我们最常见的Advice。下面是per-class的类图:

  • BeforeAdvice:在连接点前执行的横切逻辑。
  • AfterReturningAdvice:在连接点执行后,再执行横切逻辑。
  • AfterAdvice:一般由程序自己实现,当抛出异常后,执行横切逻辑。
  • AroundAdvice:Spring AOP中并没有提供这个接口,而是采用了AOP Alliance的MethodInteceptor接口;通过看AfterReturningAdvice的源码我们知道,它是不能更改连接点所在方法的返回值的(更改引用);但使用的MethodInteceptor,所有的事情,都不在话下。

在上面的类图中,还有两种类没有介绍,那就是***AdviceAdapter和***AdviceInteceptor,我们以AfterReturningAdviceInterceptor为例来说明:

该类实现了MethodInterceptor和AfterAdvice接口,同时构造函数中还有一个AfterReturningAdvice实例的参数;这个类存在的作用是为了什么呢?对,没错,Spring AOP把所有的Advice都适配成了MethodInterceptor,统一的好处是方便后面横切逻辑的执行(参看下一节),适配的工作即由***AdviceAdapter完成;

哈哈,Spring AOP的代码也不过如此嘛:所谓的AfterReturningAdvice,通过适配成MethodInterceptor后,其实就是在invoke方法中,先执行目标对象的方法,再执行的AfterReturningAdvice所定义的横切逻辑。你现在明白它为什么不能修改返回值的引用了吧?

对于per-instance的Advice,目前只有一种实现,就是Introduction,使用的场景比较少,有兴趣的同学可以自己研究一下,呵呵!

3.切面(Aspect)在Spring中,Advisor就是切面;但与通常的Aspect不同的是,Advisor通常只有一个Pointcut和一个Advice,而Aspect则可以包含多个Pointcut和多个Advice,因此Advisor是一种特殊的Aspect。但,这已经够用了!

接下来看下per-class Advisor的类图:

其实没有什么好看的,前面已经说过,Advisor包含一个Pointcut和一个Advisor;在AbstractGenericPointcutAdvisor中,持有一个Advice的引用;下面的几个实现,均是针对前面提到的几种不同的Pointcut的实现。

3.3Spring AOP实现的基本线索

我们选择ProxyFactoryBean作为入口点和分析的开始。ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,从中,可以看到一条实现AOP的基本线索。

所有的逻辑从以下的方法开始,我们主要针对 单例的代理对象 的生成:

下面我们深入到SpringAOP核心代码的内部,看看代理对象的生成机制,拦截器横切逻辑以及织入的实现。

3.4代理对象的生成

对于()方法返回了什么,这就是代理对象如何产生的逻辑了,然后一步一步,看看传说中的proxy到底是如何一步一步的产生的。

ProxyFactoryBean是AdvisedSupport的子类,Spring使用AopProxy接口把AOP代理的实现与框架的其他部分分离开来。在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮助 ,从JDK或者cglib中得到想要的代理对象:

这个DefaultAopProxyFactory是Spring用来生成AopProxy(生成代理)的地方,它包含JDK和Cglib两种实现方式。让我接着往里面看:

可以看到其中的代理对象可以由JDK或者Cglib来生成,JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,我们进入JdkDynamicAopProxy实现中看看Proxy是怎样生成的:

用Proxy包装target之后,通过ProxyFactoryBean得到对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法得到的实际上是一个Proxy,target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。

3.5 拦截器的作用

前面分析了SpringAOP实现中得到Proxy对象的过程,接下来我们去探寻Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起作用的。

还记得在JdkDynamicAopProxy中生成Proxy对象的时候,有一句这样的代码吗?

returnProxy.newProxyInstance(classLoader, proxiedInterfaces, this);

这里我们的JdkDynamicAopProxy实现了InvocationHandler这个接口,this参数对应的是符合接口的InvocationHandler对象,也就是说当 Proxy对象的函数被调用的时候,InvocationHandler的invoke方法会被作为回调函数调用:

上面所说的目标对象方法的调用,是通过AopUtils的方法调用,使用反射机制来对目标对象的方法进行的:

接下来,我们来看具体的ReflectiveMethodInvocation中proceed()方法的实现,也就是拦截器链的实现机制:

从上面的分析我们看到了Spring AOP拦截机制的基本实现,比如Spring怎样得到Proxy,怎样利用JAVAProxy以及反射机制对用户定义的拦截器链进行处理。

3.6 织入的实现

在上面调用拦截器的时候,经过一系列的注册,适配的过程以后,拦截器在拦截的时候,会调用到预置好的一个通知适配器,设置通知拦截器,这是一系列Spring设计好为通知服务的类的一个,是最终完成通知拦截和实现的地方,例如对 MethodBeforeAdviceInterceptor的实现是这样的:

可以看到通知适配器将advice适配成Interceptor以后,会调用advice的before方法去执行横切逻辑。这样就成功的完成了before通知的织入。

最后,总结一下两种方法:

再来一次,完整源码(看懂或者看不懂,都坚持一下,加深印象)。

四、Spring AOP代理源码解析

1、声明式SpringAOP代理工厂对象ProxyFactoryBean

我们以ProxyFactoryBean为例,分析Spring AOP的实现原理,ProxyFactoryBeanSpring中一个非常灵活的创建AOP应用的底层方法,封装了AOP的主要功能。

一个简单的AOP代理工厂对象的配置如下:

[html] view plaincopy

 

1. <!--配置通知器,通知器的实现定义了需要对目标对象进行的增强行为-->  

2. <bean id=”testAdvisor” class=”com.test.TestAdvisor”/>  

3. <!--配置AOP代理,封装AOP功能的主要类-->  

4. <bean id=”testAOP” class=”org.springframework.aop.ProxyFactoryBean”>  

5.     <!--AOP代理接口-->  

6.     <property name=”proxyInterfaces”>  

7.         <value>com.test.TestProxyInterface</value>  

8.     </property>  

9.     <!--需要使用AOP切面增强的对象-->  

10.     <property name=”target”>  

11.         <bean class=”com.test.TestTarget”/>  

12.     </property>  

13.     <!--代理拦截器,配置通知器的名称,即通知器在AOP代理的配置下通过使用代理对象的拦截机制发挥作用-->  

14.     <property name=”interceptorNames”>  

15.         <list>  

16.             <value>testAdvisor</value>  

17.         </list>  

18.     </property>  

19. </bean>  

2ProxyFactoryBean生成AOP Proxy代理对象:

从上面的ProxyFactoryBean的简单配置例子我们可以看出,ProxyFactoryBean是用来配置目标对象和切面行为Advice的,ProxyFactoryBean通过其配置的拦截器名称interceptorNames即通知器Advisor将切面行为Advice应用到目标对象中。

ProxyFactoryBean中,需要为待增强目标对象目标对象生成Proxy代理对象,从而为AOP切面的编织提供基础,下面通过源码分析ProxyFactoryBean的生成AOPProxy代理对象的实现过程:

(1)ProxyFactoryBean产生代理对象的主要源码:

1. public class ProxyFactoryBean extends ProxyCreatorSupport  

2.     implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {  //标注通知器为全局通用通知器  

3. public static final String GLOBAL_SUFFIX = "*";  

4. //标志通知器链是否已经完成初始化  

5. private boolean advisorChainInitialized = false;  

6. //单态模式对象  

7. private Object singletonInstance;  

8. ……  

9. //ProxyFactoryBean创建AOPProxy代理的入口方法  

10. public Object getObject() throws BeansException {  

11.         //初始化通知器链  

12.         initializeAdvisorChain();  

13.         //如果目标对象是单态模式  

14.         if (isSingleton()) {  

15.             //调用获取单态模式对象的方法产生AOPProxy代理  

16.             return getSingletonInstance();  

17.         }  

18.         //如果目标对象是原型模式  

19.         else {  

20.             if (this.targetName == null) {  

21.                 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +  

22.                         "Enable prototype proxies by setting the 'targetName' property.");  

23.             }  

24.             //调用原型模式对象方法每次创建一个新的AOPProxy代理对象  

25.             return newPrototypeInstance();  

26.         }  

27.     }  

28. //初始化通知器链  

29. private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {  

30.     //如果通知器链已经被初始化,则直接返回,即通知器链只在第一次获取代理对象时产生  

31.         if (this.advisorChainInitialized) {  

32.             return;  

33.         }  

34.         //如果ProxyFactoryBean中配置的连接器列名名称不为空  

35.         if (!ObjectUtils.isEmpty(this.interceptorNames)) {  

36.             //如果没有Bean工厂(容器)  

37.             if (this.beanFactory == null) {  

38.                 throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +  

39.                         "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));  

40.             }  

41.             //全局通知器不能是通知器链中最后一个,除非显式使用属性指定了目标  

42.             if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&  

43.                     this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {  

44.                 throw new AopConfigException("Target required after globals");  

45.             }  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值