今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道spring框架AOP的底层实现机制吗,这可是很简单的噢,我们会说,如果某个类有接口就使用JDK动态代理,没有接口就用CGLIB动态代理,并且Spring也提供了可配置开关,不管有无接口都一律使用CGLIB动态代理,例如
[html] view plain copy
print
- <aop:aspectj-autoproxy proxy-target-class="true"/>
[html] view plain copy
print
- <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
- package com.somnus.aop.framework;
- public class HelloImpl{
- public void say(String name) {
- "Hello! " + name);
- }
- }
[java] view plain copy
print
- 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) {
- new Enhancer();
- enhancer.setSuperclass(target.getClass());
- new Handler());
- enhancer.setClassLoader(target.getClass().getClassLoader());
- return enhancer.create();
- }
- public Object getProxy(Class<?> clazz){
- new Enhancer();
- enhancer.setSuperclass(clazz);
- new Handler());
- new Class[] { Serializable.class });
- return enhancer.create();
- }
- public static class Handler implements MethodInterceptor{
- private void doBefore() {
- "before method invoke");
- }
- private void doAfter() {
- "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;
- }
- }
- }
- }
[java] view plain copy
print
- package com.somnus.aop.framework;
- public class CglibClient {
- public static void main(String[] args) {
- new CglibProxy();
- new HelloImpl());
- "Somnus");
- "*****************************************************************");
- class);
- "Somnus");
- }
- }
是否似曾相似呢,和CGLIB对比,可是仔细看API的包名,却发现并不是
[java] view plain copy
print
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
但我可以很负责任的告诉你,Spring只是对它们做了封装,因为随后说到的Spring在用到CGLIB动态代理都是用到自己的封装类。
现在我抛砖引玉,用Spring框架的AOP做一个前置通知、后置通知,分别用xml配置的方式和编程式
先提供要用到的接口和实现类
[java] view plain copy
print
- public interface GreetingInterface {
- String sayHello(String name);
- }
[java] view plain copy
print
- public class GreetingImpl implements GreetingInterface {
- @Override
- public String sayHello(String name) {
- "Hello! " + name);
- return name;
- }
- }
a:编程式
[java] view plain copy
print
- 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 {
- ">>>>>>>>>>>>>>>>>>Before Start>>>>>>>>>>>>>>>>>>");
- "Method Name: " + method.getName());
- "args:" + Arrays.toString(args));
- "Target : " + target.getClass().getName());
- "<<<<<<<<<<<<<<<<<<Before End<<<<<<<<<<<<<<<<<<<<<<<<");
- }
- }
[java] view plain copy
print
- 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 {
- ">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");
- "returnValue:" + returnValue);
- "Method Name: " + method.getName());
- "args:" + Arrays.toString(args));
- "Target : " + target.getClass().getName());
- "<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");
- }
- }
[java] view plain copy
print
- 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 {
- ">>>>>>>>>>>>>>>>After Start>>>>>>>>>>>>>>>>>>>>");
- "returnValue:" + returnValue);
- "Method Name: " + method.getName());
- "args:" + Arrays.toString(args));
- "Target : " + target.getClass().getName());
- "<<<<<<<<<<<<<<<<<After End<<<<<<<<<<<<<<<<<<<<<<");
- }
- }
[java] view plain copy
print
- package com.somnus.aop;
- import org.springframework.aop.framework.ProxyFactory;
- public class BeforeAndAfterClient {
- public static void main(String[] args) {
- new ProxyFactory(); // 创建代理工厂
- new GreetingImpl()); // 射入目标类对象
- new GreetingBeforeAdvice()); // 添加前置增强
- new GreetingAfterAdvice()); // 添加后置增强
- // 从代理工厂中获取代理
- "Jack"); // 调用代理的方法
- }
- }
上面曾说到过可设置是否强制使用CGLIB动态代理开关,如果采用这种方式将怎么做
[java] view plain copy
print
- proxyFactory.setOptimize(true);
b:XML配置形式
[html] view plain copy
print
?
- <?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"/>
[java] view plain copy
print
- 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 对象(其实就是一个代理)*/
- "greetingProxy");
- "Jack");
- }
- }
其实无论是何种实现方式,它们最终要做的都是动态代理,也许用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
- public static Object newProxyInstance(ClassLoader loader,
- <?>[] interfaces,
- InvocationHandler h)throws IllegalArgumentException{
- //具体实现,我这里省略,你可以去翻看源码
- }
这个方法需要三个参数: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这个类