Spring框架AOP源码剖析

今天我要和大家分享的是 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");
	}
}

是否似曾相似呢,和CGLIB对比,可是仔细看API的包名,却发现并不是
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);

b:XML配置形式

<?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"); 
	}
}

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

Spring框架AOP源码剖析_第1张图片

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

我们看下DefaultAopProxyFactory这个类的源码

Spring框架AOP源码剖析_第2张图片

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

Spring框架AOP源码剖析_第3张图片


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


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

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

Spring框架AOP源码剖析_第4张图片

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

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

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


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

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


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

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

Spring框架AOP源码剖析_第8张图片

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


你可能感兴趣的:(Spring框架AOP源码剖析)