您的位置:首页 > 编程语言 > Java开发

Spring AOP高级——源码实现(1)动态代理技术

2017-11-09 23:12 507 查看
jdk1.8.0_144  

  在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理。 动态代理实际上也是一种结构型的设计模式,JDK中已经为我们准备好了这种设计模式,不过这种JDK为我们提供的动态代理有2个缺点:

只能代理实现了接口的目标对象;

基于反射,效率低

  鉴于以上2个缺点,于是就出现了第二种动态代理技术——CGLIB(Code Generation Library)。这种代理技术一是不需要目标对象实现接口(这大大扩展了使用范围),二是它是基于字节码实现(这比反射效率高)。当然它并不是完全没有缺点,因为它不能代理final方法(因为它的动态代理实际是生成目标对象的子类)。

  Spring AOP中生成代理对象时既可以使用JDK的动态代理技术,也可以使用CGLIB的动态代理技术,本章首先对这两者动态代理技术做简要了解,便于后续源码的理解。

JDK动态代理技术

  JDK动态代理技术首先要求我们目标对象需要实现一个接口:

package proxy;

/**
* Created by Kevin on 2017/11/8.
*/
public interface Subject {
void sayHello();
}


  接下来就是我们需要代理的真实对象,即目标对象:

package proxy;

/**
* 目标对象,即需要被代理的对象
* Created by Kevin on 2017/11/8.
*/
public class RealSubject implements Subject{
public void sayHello() {
System.out.println("hello world");
}
}


  这是一个真实的对象,我们希望在不更改原有代码逻辑的基础上增强该类的sayHello方法,利用JDK动态代理技术需要我们实现InvocationHandler接口中的invoke方法:

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxySubject implements InvocationHandler {
private Object target;

public ProxySubject(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前");
Object object = method.invoke(target, args);
System.out.println("调用后");
return object;
}
}


  第15行,在invoke方法中可以看到,在调用目标对象的方法前后我们对方法进行了增加,这其实就是AOP中Before和After通知的奥义所在。

  加入测试代码:

package proxy;

import java.lang.reflect.Proxy;

/**
* Created by Kevin on 2017/11/8.
*/
public class Test {
public static void main(String[] args) {
Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), new ProxySubject(new RealSubject()));
subject.sayHello();

//查看subject对象的类型
System.out.println(subject.getClass().getName());
}
}


  执行结果:

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>


View Code
  前面提到了CGLib动态代理技术不需要目标对象实现自一个接口:

package cglibproxy;

/**
* 目标对象(需要被代理的类)
* Created by Kevin on 2017/11/6.
*/
public class RealSubject {
public void sayHello() {
System.out.println("hello");
}
}


  下面我们就使用CGLib代理这个类:

package cglibproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* 代理类
* Created by Kevin on 2017/11/6.
*/
public class ProxySubject implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();    //用于创建无参的目标对象代理类,对于有参构造器则调用Enhancer.create(Class[] argumentTypes, Object[] arguments),第一个参数表示参数类型,第二个参数表示参数的值。
}

@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("调用前");
Object result = methodProxy.invokeSuper(object, args);
System.out.println("调用后");
return result;
}
}


  可以看到同样是需要实现一个接口——MethodIntercept,并且实现一个和invoke类似的方法——intercept。

  加入测试代码:

package cglibproxy;

/**
* Created by Kevin on 2017/11/6.
*/
public class Main {
public static void main(String[] args) {
RealSubject subject = (RealSubject) new ProxySubject().getProxy(RealSubject.class);
subject.sayHello();
System.out.println(subject.getClass().getName());
}
}


  执行结果:



  可以看到的执行结果和JDK动态代理的结果一样,不同的是代理类的类型是cglibproxy.RealSubject$$EnhancerByCGLIB$$cb568e93。接着我们来看CGLib是如何生成代理类的。

  生成代理类的是ProxySubject类中的getProxy方法,而其中又是传入两个参数:

enhancer.setSuperclass(clazz);    //设置需要代理的类
enhancer.setCallback(this);    //设置回调方法


  参数设置好后就调用enhancer.create()方法创建代理类。

public Object create() {
classOnly = false;    //这个字段设置为false表示返回的是具体的Object代理类,在createClass()方法中设置的是classOnly=true表示的返回class类型的代理类。
argumentTypes = null;        //创建的是无参目标对象的代理类,故没有参数,所以参数类型设置为null
return createHelper();
}


  看来还在调用一个叫createHelper的方法。

private Object createHelper() {
preValidate();        //提前作一些校验
//
……
}


private void preValidate() {
if (callbackTypes == null) {
callbackTypes = CallbackInfo.determineTypes(callbacks, false);
validateCallbackTypes = true;
}    //检查回调方法是否为空
if (filter == null) {    //检查是否设置过滤器,如果设置了多个回调方法就需要设置过滤器
if (callbackTypes.length > 1) {
throw new IllegalStateException("Multiple callback types possible but no filter specified");
}
filter = ALL_ZERO;
}
}


  接着查看createHelper的剩余代码:

private Object createHelper() {
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;        //在CGLib中也使用到了缓存机制,这段代码也比较复杂,有关缓存的策略暂时也不做分析吧
Object result = super.create(key);    //利用字节实现并创建代理类对象
return result;
}


  马马虎虎地只能说是介绍了JDK与CGLib两种动态代理技术,并没有很深入地研究,特别是在两者在缓存机制上的实现,略感遗憾。

  另外,在开头提到了CGLib的性能比JDK高,这实际上并不准确。或许这在特别条件下的确如此,因为在我实测发现JDK8的动态代理效率非常高,甚至略高于CGLib,但是在JDK6的环境下的效率就显得比较低了。所以,通常所说的CGLib性能比JDK动态代理要高,是传统的挂念,实际上Java一直都在不断优化动态代理性能,在比较高版本的JDK条件下可以放行大胆的使用JDK原生的动态代理。

这是一个能给程序员加buff的公众号

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: