今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道Spring框架AOP的底层实现机制吗,这可是很简单的噢,我们会说,如果某个类有接口就使用JDK动态代理,没有接口就用CGLIB动态代理,并且Spring也提供了可配置开关,不管有无接口都一律使用CGLIB动态代理,例如
<aop:aspectj-autoproxy proxy-target-class="true"/>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
默认都是false,也就是使用JDK动态代理,但是如果没有接口也还是会使用CGLIB动态代理的,当然啦,这里设为了true,也就是在任何情况下都只使用CGLIB动态代理,但是你是否真正想过,Spring的底层是如何控制该用哪个动态代理的
那我们如何用Spring实现一个CGLIB动态代理呢(Spring框架AOP实现用到的net.sf.cglib并不是直接使用),而是对其相关API做了封装,
我们可以看如下的例子了解
package com.somnus.aop.framework; public class HelloImpl{ public void say(String name) { System.out.println("Hello! " + name); } }
package com.somnus.aop.framework; import java.io.Serializable; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; public class CglibProxy implements Serializable { private static final long serialVersionUID = 1L; public Object getProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new Handler()); enhancer.setClassLoader(target.getClass().getClassLoader()); return enhancer.create(); } public Object getProxy(Class<?> clazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(new Handler()); enhancer.setInterfaces(new Class[] { Serializable.class }); return enhancer.create(); } public static class Handler implements MethodInterceptor{ private void doBefore() { System.out.println("before method invoke"); } private void doAfter() { System.out.println("after method invoke"); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { try { // 调用之前 doBefore(); // 调用原始对象的方法 Object result = proxy.invokeSuper(obj, args); // 调用之后 doAfter(); return result; } catch (Throwable e) { throw e; } } } }
package com.somnus.aop.framework; public class CglibClient { public static void main(String[] args) { CglibProxy cglib = new CglibProxy(); HelloImpl proxy = (HelloImpl)cglib.getProxy(new HelloImpl()); proxy.say("Somnus"); System.out.println("*****************************************************************"); HelloImpl proxy2 = (HelloImpl)cglib.getProxy(HelloImpl.class); proxy2.say("Somnus"); } }
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
但我可以很负责任的告诉你,Spring只是对它们做了封装,因为随后说到的Spring在用到CGLIB动态代理都是用到自己的封装类。
现在我抛砖引玉,用Spring框架的AOP做一个前置通知、后置通知,分别用xml配置的方式和编程式
先提供要用到的接口和实现类
public interface GreetingInterface { String sayHello(String name); }
public class GreetingImpl implements GreetingInterface { @Override public String sayHello(String name) { System.out.println("Hello! " + name); return name; } }
a:编程式
package com.somnus.aop; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.MethodBeforeAdvice; public class GreetingBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(">>>>>>>>>>>>>>>>>>Before Start>>>>>>>>>>>>>>>>>>"); System.out.println("Method Name: " + method.getName()); System.out.println("args:" + Arrays.toString(args)); System.out.println("Target : " + target.getClass().getName()); System.out.println("<<<<<<<<<<<<<<<<<<Before End<<<<<<<<<<<<<<<<<<<<<<<<"); } }
package com.somnus.aop; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.AfterReturningAdvice; public class GreetingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>"); System.out.println("returnValue:" + returnValue); System.out.println("Method Name: " + method.getName()); System.out.println("args:" + Arrays.toString(args)); System.out.println("Target : " + target.getClass().getName()); System.out.println("<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<"); } }
package com.somnus.aop; import org.springframework.aop.framework.ProxyFactory; public class BeforeAndAfterClient { public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory(); // 创建代理工厂 proxyFactory.setTarget(new GreetingImpl()); // 射入目标类对象 proxyFactory.addAdvice(new GreetingBeforeAdvice()); // 添加前置增强 proxyFactory.addAdvice(new GreetingAfterAdvice()); // 添加后置增强 GreetingInterface greeting = (GreetingInterface) proxyFactory.getProxy(); // 从代理工厂中获取代理 greeting.sayHello("Jack"); // 调用代理的方法 } }
上面曾说到过可设置是否强制使用CGLIB动态代理开关,如果采用这种方式将怎么做
proxyFactory.setOptimize(true);
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="greetingImpl" class="com.somnus.xml.aop.GreetingImpl"></bean> <!-- 配置一个代理 --> <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.somnus.xml.aop.GreetingInterface"/> <!-- 需要代理的接口 --> <property name="target" ref="greetingImpl"/> <!-- 目标接口实现类 --> <property name="interceptorNames"> <!-- 拦截器名称(也就是增强类名称,Spring Bean 的 id) --> <list> <value>greetingBeforeAdvice</value> <value>greetingAfterReturningAdvice</value> </list> </property> </bean> </beans>
XML配置强制使用CGLIB动态代理开关,应加上
<property name="optimize" value="true"/>
package com.somnus.xml.aop; import org.junit.Test; import com.somnus.AbstractTestSupport; import com.somnus.ApplicationContextHolder; public class SpringTest extends AbstractTestSupport { @Test public void save(){ /**从 Context 中根据 id 获取 Bean 对象(其实就是一个代理)*/ GreetingInterface greeting = (GreetingInterface) ApplicationContextHolder.getBean("greetingProxy"); greeting.sayHello("Jack"); } }
在上面结构图中与标准的策略模式结构稍微有点不同,这里抽象策略是 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 就是创建代理对象的方法。这个方法的源码如下
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{ //具体实现,我这里省略,你可以去翻看源码 }这个方法需要三个参数:ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用于执行 除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。这在后面再详解。 下面还是看看 Proxy 如何产生代理类的过程,它构造出来的代理类到底是什么样子?下面揭晓啦。
其实从上图中可以发现正在构造代理类的是在 ProxyGenerator 的 generateProxyClass 的方法中。ProxyGenerator 类在 sun.misc 包下,感兴趣的话可以看看它的源码。
Spring 创建了代理对象后,当你调用目标对象上的方法时,将都会被代理到 InvocationHandler 类的invoke 方法中执行,这在前面已经解释。在这里 JdkDynamicAopProxy 类实现了 InvocationHandler 接 口。 下面再看看 Spring 是如何调用拦截器的,下面是这个过程的时序图:
以上所说的都是 Jdk 动态代理,CGLIB动态代理源码,如果你感兴趣,也可以去翻翻CglibAopProxy这个类