Spring框架的AOP源码剖析

来源:互联网 发布:深圳淘宝代运营 编辑:程序博客网 时间:2024/06/15 20:41

今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道spring框架AOP的底层实现机制吗,这可是很简单的噢,我们会说,如果某个类有接口就使用JDK动态代理,没有接口就用CGLIB动态代理,并且Spring也提供了可配置开关,不管有无接口都一律使用CGLIB动态代理,例如

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <aop:aspectj-autoproxy proxy-target-class="true"/>  

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>  


默认都是false,也就是使用JDK动态代理,但是如果没有接口也还是会使用CGLIB动态代理的,当然啦,这里设为了true,也就是在任何情况下都只使用CGLIB动态代理,但是你是否真正想过,Spring的底层是如何控制该用哪个动态代理的

那我们如何用Spring实现一个CGLIB动态代理呢(Spring框架AOP实现用到的net.sf.cglib并不是直接使用),而是对其相关API做了封装,

我们可以看如下的例子了解

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop.framework;  
  2. public class HelloImpl{  
  3.     public void say(String name) {  
  4.         System.out.println("Hello! " + name);  
  5.     }  
  6. }  
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop.framework;  
  2. import java.io.Serializable;  
  3. import java.lang.reflect.Method;  
  4. import org.springframework.cglib.proxy.Enhancer;  
  5. import org.springframework.cglib.proxy.MethodInterceptor;  
  6. import org.springframework.cglib.proxy.MethodProxy;  
  7. public class CglibProxy implements Serializable {  
  8.     private static final long serialVersionUID = 1L;  
  9.     public Object getProxy(Object target) {  
  10.         Enhancer enhancer = new Enhancer();  
  11.         enhancer.setSuperclass(target.getClass());  
  12.         enhancer.setCallback(new Handler());  
  13.         enhancer.setClassLoader(target.getClass().getClassLoader());  
  14.         return enhancer.create();  
  15.     }  
  16.     public Object getProxy(Class<?> clazz){  
  17.         Enhancer enhancer = new Enhancer();  
  18.         enhancer.setSuperclass(clazz);  
  19.         enhancer.setCallback(new Handler());  
  20.         enhancer.setInterfaces(new Class[] { Serializable.class });  
  21.         return enhancer.create();  
  22.     }  
  23.     public static class Handler implements MethodInterceptor{  
  24.         private void doBefore() {  
  25.             System.out.println("before method invoke");  
  26.         }  
  27.         private void doAfter() {  
  28.             System.out.println("after method invoke");  
  29.         }  
  30.         @Override  
  31.         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
  32.             try {  
  33.                 // 调用之前  
  34.                 doBefore();  
  35.                 // 调用原始对象的方法  
  36.                 Object result = proxy.invokeSuper(obj, args);  
  37.                 // 调用之后  
  38.                 doAfter();  
  39.                 return result;  
  40.             } catch (Throwable e) {  
  41.                 throw e;  
  42.             }  
  43.         }  
  44.     }  
  45. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop.framework;  
  2. public class CglibClient {  
  3.     public static void main(String[] args) {  
  4.         CglibProxy cglib = new CglibProxy();  
  5.         HelloImpl proxy = (HelloImpl)cglib.getProxy(new HelloImpl());  
  6.         proxy.say("Somnus");  
  7.         System.out.println("*****************************************************************");  
  8.         HelloImpl proxy2 = (HelloImpl)cglib.getProxy(HelloImpl.class);  
  9.         proxy2.say("Somnus");  
  10.     }  
  11. }  


是否似曾相似呢,和CGLIB对比,可是仔细看API的包名,却发现并不是
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. import net.sf.cglib.proxy.Enhancer;  
  2. import net.sf.cglib.proxy.MethodInterceptor;  
  3. import net.sf.cglib.proxy.MethodProxy;  


但我可以很负责任的告诉你,Spring只是对它们做了封装,因为随后说到的Spring在用到CGLIB动态代理都是用到自己的封装类。

现在我抛砖引玉,用Spring框架的AOP做一个前置通知、后置通知,分别用xml配置的方式和编程式

先提供要用到的接口和实现类

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public interface GreetingInterface {  
  2.     String sayHello(String name);  
  3. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public class GreetingImpl implements GreetingInterface {  
  2.     @Override  
  3.     public String sayHello(String name) {  
  4.         System.out.println("Hello! " + name);  
  5.         return name;  
  6.     }  
  7. }  


a:编程式

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop;  
  2. import java.lang.reflect.Method;  
  3. import java.util.Arrays;  
  4. import org.springframework.aop.MethodBeforeAdvice;  
  5. public class GreetingBeforeAdvice implements MethodBeforeAdvice{  
  6.     @Override  
  7.     public void before(Method method, Object[] args, Object target) throws Throwable {  
  8.         System.out.println(">>>>>>>>>>>>>>>>>>Before Start>>>>>>>>>>>>>>>>>>");  
  9.         System.out.println("Method Name: " + method.getName());  
  10.         System.out.println("args:" + Arrays.toString(args));  
  11.         System.out.println("Target : " + target.getClass().getName());  
  12.         System.out.println("<<<<<<<<<<<<<<<<<<Before End<<<<<<<<<<<<<<<<<<<<<<<<");  
  13.     }  
  14. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop;  
  2. import java.lang.reflect.Method;  
  3. import java.util.Arrays;  
  4. import org.springframework.aop.AfterReturningAdvice;  
  5. public class GreetingAfterAdvice implements AfterReturningAdvice {  
  6.     @Override  
  7.     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {  
  8.         System.out.println(">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");  
  9.         System.out.println("returnValue:" + returnValue);  
  10.         System.out.println("Method Name: " + method.getName());  
  11.         System.out.println("args:" + Arrays.toString(args));  
  12.         System.out.println("Target : " + target.getClass().getName());  
  13.         System.out.println("<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");  
  14.     }  
  15. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop;  
  2. import java.lang.reflect.Method;  
  3. import java.util.Arrays;  
  4. import org.springframework.aop.AfterReturningAdvice;  
  5. public class GreetingAfterAdvice implements AfterReturningAdvice {  
  6.     @Override  
  7.     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {  
  8.         System.out.println(">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");  
  9.         System.out.println("returnValue:" + returnValue);  
  10.         System.out.println("Method Name: " + method.getName());  
  11.         System.out.println("args:" + Arrays.toString(args));  
  12.         System.out.println("Target : " + target.getClass().getName());  
  13.         System.out.println("<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");  
  14.     }  
  15. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.aop;  
  2. import org.springframework.aop.framework.ProxyFactory;  
  3. public class BeforeAndAfterClient {  
  4.     public static void main(String[] args) {  
  5.         ProxyFactory proxyFactory = new ProxyFactory();     // 创建代理工厂  
  6.         proxyFactory.setTarget(new GreetingImpl());         // 射入目标类对象  
  7.         proxyFactory.addAdvice(new GreetingBeforeAdvice()); // 添加前置增强  
  8.         proxyFactory.addAdvice(new GreetingAfterAdvice());  // 添加后置增强   
  9.    
  10.         GreetingInterface greeting = (GreetingInterface) proxyFactory.getProxy(); // 从代理工厂中获取代理  
  11.         greeting.sayHello("Jack");                              // 调用代理的方法  
  12.     }  
  13. }  

上面曾说到过可设置是否强制使用CGLIB动态代理开关,如果采用这种方式将怎么做

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. proxyFactory.setOptimize(true);  


b:XML配置形式

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
  5.     <bean id="greetingImpl" class="com.somnus.xml.aop.GreetingImpl"></bean>  
  6.     <!-- 配置一个代理 -->  
  7.     <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  8.         <property name="interfaces" value="com.somnus.xml.aop.GreetingInterface"/>    <!-- 需要代理的接口 -->  
  9.         <property name="target" ref="greetingImpl"/>                                  <!-- 目标接口实现类 -->  
  10.         <property name="interceptorNames">        <!-- 拦截器名称(也就是增强类名称,Spring Bean 的 id) -->  
  11.             <list>  
  12.                 <value>greetingBeforeAdvice</value>  
  13.                 <value>greetingAfterReturningAdvice</value>  
  14.             </list>  
  15.         </property>  
  16.     </bean>  
  17. </beans>  


XML配置强制使用CGLIB动态代理开关,应加上

<property name="optimize" value="true"/>

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package com.somnus.xml.aop;  
  2. import org.junit.Test;  
  3. import com.somnus.AbstractTestSupport;  
  4. import com.somnus.ApplicationContextHolder;  
  5. public class SpringTest extends AbstractTestSupport {  
  6.     @Test  
  7.     public void save(){  
  8.         /**从 Context 中根据 id 获取 Bean 对象(其实就是一个代理)*/  
  9.         GreetingInterface greeting = (GreetingInterface) ApplicationContextHolder.getBean("greetingProxy");   
  10.         greeting.sayHello("Jack");   
  11.     }  
  12. }  


其实无论是何种实现方式,它们最终要做的都是动态代理,也许用JDK动态代理,也许用CGLIB动态代理,至于它们是何种关系,可参考我下面给的类图


在上面结构图中与标准的策略模式结构稍微有点不同,这里抽象策略是 AopProxy 接口,Cglib2AopProxy 和 JdkDynamicAopProxy 分别代表两种策略的实现方式,DefaultAopProxyFactory就是代表Context 角色 ,它根据条件选择使用 Jdk 代理方式还是 CGLIB 方式,而另外三个类主要是来负责创建具体策略对象,ProxyFactoryBean 是通过依赖的方法来关联具体策略对象的,它是通过调用策略对象的getProxy (ClassLoaderclassLoader)方法来完成操作。 

我们看下DefaultAopProxyFactory这个类的源码


ProxyCreatorSupport类负责创建AopProxy对象,当然就是它调用createAopProxy方法,看代码



this代表谁呢,看到我给的类图,ProxyFactory、ProxyFactoryBean可都是继承ProxyCreatorSupport的,那在我们刚才的demo中似乎都看到了都对ProxyFactory、ProxyFactoryBean做了什么吧,注入了诸如接口、目标类、通知(拦截器)的东西,你可以最后这些东西都被利用起来了,承担它们本承担的责任。


最后我们再讲下JDK动态代理,在AOP中是如何工作的

首先我们必须先了解的动态代理的原理,因为 AOP 就是基于动态代理实现的。动态代理还要从 JDK 本身说起。 在 Jdk 的 Java.lang.reflect 包下有个 Proxy 类,它正是构造代理类的入口。这个类的结构入下: 


从上图发现只有四个是公有方法。而最后一个方法 newProxyInstance 就是创建代理对象的方法。这个方法的源码如下

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                           Class<?>[] interfaces,  
  3.                                           InvocationHandler h)throws IllegalArgumentException{  
  4. //具体实现,我这里省略,你可以去翻看源码  
  5. }  
这个方法需要三个参数:ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用于执行 除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。这在后面再详解。 下面还是看看 Proxy 如何产生代理类的过程,它构造出来的代理类到底是什么样子?下面揭晓啦。 

其实从上图中可以发现正在构造代理类的是在 ProxyGenerator 的 generateProxyClass 的方法中。ProxyGenerator 类在 sun.misc 包下,感兴趣的话可以看看它的源码。 


从前面代理的原理我们知道,代理的目的是调用目标方法时我们可以转而执行 InvocationHandler 类的 invoke 方法,所以如何在 InvocationHandler 上做文章就是 Spring 实现 Aop 的关键所在。 Spring 的 Aop 实现是遵守 Aop 联盟的约定。同时 Spring 又扩展了它,增加了如 Pointcut、Advisor等一些接口使得更加灵活

下面是 Jdk 动态代理的类图: 



 下面看看 Spring 如何完成了代理以及是如何调用拦截器的。 前面提到 Spring Aop 也是实现其自身的扩展点来完成这个特性的,从这个代理类可以看出它正是继承了 Factory Bean 的 ProxyFactoryBean,FactoryBean 之所以特别就在它可以让你自定义对象的创建 方法。当然代理对象要通过 Proxy 类来动态生成。 下面是 Spring 创建的代理对象的时序图:

Spring 创建了代理对象后,当你调用目标对象上的方法时,将都会被代理到 InvocationHandler 类的invoke 方法中执行,这在前面已经解释。在这里 JdkDynamicAopProxy 类实现了 InvocationHandler 接 口。 下面再看看 Spring 是如何调用拦截器的,下面是这个过程的时序图: 


以上所说的都是 Jdk 动态代理,CGLIB动态代理源码,如果你感兴趣,也可以去翻翻CglibAopProxy这个类


0 0
原创粉丝点击