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


[html]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  1. <aop:aspectj-autoproxy proxy-target-class="true"/>  



[html]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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​

​​


  1. package com.somnus.aop.framework;  
  2. public class HelloImpl{  
  3. public void say(String name) {  
  4. "Hello! " + name);  
  5.     }  
  6. }  


[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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. new Enhancer();  
  11.         enhancer.setSuperclass(target.getClass());  
  12. new Handler());  
  13.         enhancer.setClassLoader(target.getClass().getClassLoader());  
  14. return enhancer.create();  
  15.     }  
  16. public Object getProxy(Class<?> clazz){  
  17. new Enhancer();  
  18.         enhancer.setSuperclass(clazz);  
  19. new Handler());  
  20. new Class[] { Serializable.class });  
  21. return enhancer.create();  
  22.     }  
  23. public static class Handler implements MethodInterceptor{  
  24. private void doBefore() {  
  25. "before method invoke");  
  26.         }  
  27. private void doAfter() {  
  28. "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​

​​


  1. package com.somnus.aop.framework;  
  2. public class CglibClient {  
  3. public static void main(String[] args) {  
  4. new CglibProxy();  
  5. new HelloImpl());  
  6. "Somnus");  
  7. "*****************************************************************");  
  8. class);  
  9. "Somnus");  
  10.     }  
  11. }  





是否似曾相似呢,和CGLIB对比,可是仔细看API的包名,却发现并不是


[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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​

​​


  1. public interface GreetingInterface {  
  2.     String sayHello(String name);  
  3. }  



[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  1. public class GreetingImpl implements GreetingInterface {  
  2. @Override  
  3. public String sayHello(String name) {  
  4. "Hello! " + name);  
  5. return name;  
  6.     }  
  7. }  




a:编程式


[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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. ">>>>>>>>>>>>>>>>>>Before Start>>>>>>>>>>>>>>>>>>");  
  9. "Method Name: " + method.getName());  
  10. "args:" + Arrays.toString(args));  
  11. "Target : " + target.getClass().getName());  
  12. "<<<<<<<<<<<<<<<<<<Before End<<<<<<<<<<<<<<<<<<<<<<<<");  
  13.     }  
  14. }  




[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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. ">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");  
  9. "returnValue:" + returnValue);  
  10. "Method Name: " + method.getName());  
  11. "args:" + Arrays.toString(args));  
  12. "Target : " + target.getClass().getName());  
  13. "<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");  
  14.     }  
  15. }  



[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  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. ">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");  
  9. "returnValue:" + returnValue);  
  10. "Method Name: " + method.getName());  
  11. "args:" + Arrays.toString(args));  
  12. "Target : " + target.getClass().getName());  
  13. "<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");  
  14.     }  
  15. }  



[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  1. package com.somnus.aop;  
  2. import org.springframework.aop.framework.ProxyFactory;  
  3. public class BeforeAndAfterClient {  
  4. public static void main(String[] args) {  
  5. new ProxyFactory();     // 创建代理工厂  
  6. new GreetingImpl());         // 射入目标类对象  
  7. new GreetingBeforeAdvice()); // 添加前置增强  
  8. new GreetingAfterAdvice());  // 添加后置增强   
  9.    
  10. // 从代理工厂中获取代理  
  11. "Jack");                              // 调用代理的方法  
  12.     }  
  13. }  



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



[java]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  1. proxyFactory.setOptimize(true);  




b:XML配置形式



[html]  ​​view plain​​  ​​copy​​


 ​​print​

​​?​

  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​

​​


  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. "greetingProxy");   
  10. "Jack");   
  11.     }  
  12. }  




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



Spring框架的AOP源码剖析_System

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

我们看下DefaultAopProxyFactory这个类的源码


Spring框架的AOP源码剖析_java_02

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


Spring框架的AOP源码剖析_spring_03

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

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

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


Spring框架的AOP源码剖析_动态代理_04

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


[html]  ​​view plain​​  ​​copy​​


 ​​print​

​​


  1. public static Object newProxyInstance(ClassLoader loader,  
  2. <?>[] interfaces,  
  3.                                           InvocationHandler h)throws IllegalArgumentException{  
  4. //具体实现,我这里省略,你可以去翻看源码  
  5. }  


这个方法需要三个参数:ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用于执行 除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。这在后面再详解。 下面还是看看 Proxy 如何产生代理类的过程,它构造出来的代理类到底是什么样子?下面揭晓啦。 


Spring框架的AOP源码剖析_动态代理_05


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


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



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






Spring框架的AOP源码剖析_spring_06



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


Spring框架的AOP源码剖析_System_07


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


Spring框架的AOP源码剖析_动态代理_08

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