代理模式 --- 静态代理与动态代理;动态代理的实现原理;手写实现自己的动态代理;比较jdk与cglib;

这篇文章,主要解决以下几个问题:

  1. 分析静态代理存在的问题。
  2. 分析jdk动态代理执行过程和实现原理。
  3. 动手实现自己的动态代理。
  4. 分析CGlib的动态代理实现原理。
  5. 比较CGlib动态代理和jdk动态代理。

1 静态代理及存在的问题

1.1 静态代理

在实际的开发中,我们可能会遇到这种问题,如:现在有一个订单的服务接口,以及其实现类如下:

/**
 * 订单服务
 */
public interface OrderService{
    /**
     * 创建订单
     * @throws InterruptedException
     */
    void createOrder() throws InterruptedException;
}
/**
 * 订单服务实现
 */
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder() {
        System.out.println("创建订单");
    }
}

我们现在有新的需求,需要知道这个服务中createOrder方法的执行时间,我们会怎么做呢,最简单最容易的想法就是去修改createOrder方法,或修改调用createOrder方法的代码,在调用createOrder前后增加一些逻辑。然而这样需要修改源代码,不符合开闭原则。

我们可以通过静态代理的方式去实现这个需求,具体做法如下:

  1. 创建一个代理类OrderServiceProxy,实现OrderService。
  2. OrderServiceProxy中持有被代理对象的引用OrderService,创建OrderServiceProxy代理对象的时候,需要告诉OrderServiceProxy被代理的对象是谁。
  3. 代理类OrderServiceProxy中,重写OrderService接口方法。接口中调用具体的被代理对象的相应方法,并且可以在被代理对象方法前后,异常捕获catch,finally等位置增加逻辑。这里只在方法调用的前后增加逻辑完成打印方法执行时间的功能。

前三步实现如下:

public class OrderServiceProxy implements OrderService {
    /**
     * 代理对象持有被代理对象的引用
     */
    private OrderService orderService;
    private Long startTime = 0L;

    /**
     * 创建代理对象的时候,传入被代理的对象
     * @param orderService
     */
    public OrderServiceProxy(OrderService orderService){
        this.orderService = orderService;
    }

    @Override
    public void createOrder() {
        beforeTime("createOrder");
        //调用被代理对象的方法
        orderService.createOrder();
        afterTime("createOrder");
    }
    /**
     * 方法调用之前执行
     * @param methodName
     */
    public void beforeTime(String methodName){
        startTime = System.currentTimeMillis();
        System.out.println(methodName + "开始执行时间:" + startTime);
    }
    /**
     * 方法调用之后执行
     * @param methodName
     */
    public void afterTime(String methodName){
        System.out.println(methodName + "执行总共用时:" + (System.currentTimeMillis() - startTime));
    }
}
  1. 最后,具体调用地方创建OrderService对象的时候,创建的是一个代理类对象OrderServiceProxy,并且告诉OrderServiceProxy具体代理的对象是谁(这里是OrderService实现类OrderServiceImpl)
public class StaticProxyTest {
    public static void main(String[] args) throws Exception{
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.createOrder();
    }
}

这样就完成了,程序执行createOrder的时候,调用过程如下:

  1. mian方法中调用的是代理类OrderServiceProxy的createOrder方法
  2. 在OrderServiceProxy的createOrder中,先调用增加的逻辑before方法
  3. 然后调用具体的被代理类的createOrder方法
  4. 最后再调用增加的逻辑after方法。

这样就起到了对被代理类OrderServiceImpl功能的增强。

执行结果如下:

执行结果

1.2 静态代理存在问题

(1)如果OrderService中增加新的功能,如增加queryOrder方法,此时,代理类也需要手动实现新增加的方法。如下:

/**
 * 订单服务
 */
public interface OrderService{
    /**
     * 创建订单
     * @throws InterruptedException
     */
    void createOrder();

    /**
     * 查询订单
     */
    void queryOrder();
}
public class OrderServiceProxy implements OrderService {
    /**
     * 代理对象持有被代理对象的引用
     */
    private OrderService orderService;
    private Long startTime = 0L;

    /**
     * 创建代理对象的时候,传入被代理的对象
     * @param orderService
     */
    public OrderServiceProxy(OrderService orderService){
        this.orderService = orderService;
    }

    @Override
    public void createOrder() {
        beforeTime("createOrder");
        //调用被代理对象的方法
        orderService.createOrder();
        afterTime("createOrder");
    }

    @Override
    public void queryOrder() {
        orderService.createOrder();
    }

    /**
     * 方法调用之前执行
     * @param methodName
     */
    public void beforeTime(String methodName){
        startTime = System.currentTimeMillis();
        System.out.println(methodName + "开始执行时间:" + startTime);
    }
    /**
     * 方法调用之后执行
     * @param methodName
     */
    public void afterTime(String methodName){
        System.out.println(methodName + "执行总共用时" + (System.currentTimeMillis() - startTime));
    }
}

(2)现在不仅仅有订单服务,还有支付服务,同样支付服务业需要知道支付过程中方法的性能,需要打印时间。此时我们需要为支付服务手动增加一个代理类,在这个代理类中手动实现所有的支付接口,并手动为每个方法增加before和after方法打印方法的执行时间。如下支付接口:

/**
 * 支付服务
 */
public interface PaymentService {
    /**
     * 支付
     */
    void pay();
}
/**
 * 支付服务实现
 */
public class PaymentServiceImpl implements PaymentService {
    @Override
    public void pay() {
        System.out.println("支付");
    }
}

显然,OrderServiceProxy是不能够代理支付的服务,因为OrderServiceProxy中持有的是订单服务OrderService的引用。

所以,需要为 PaymentService创建一个自己的代理类 PaymentServiceProxy,实现 PaymentService接口,并持有 PaymentService接口引用,如下:

public class PaymentServiceProxy implements PaymentService {
    private PaymentService paymentService;
    private Long startTime = 0L;
    public PaymentServiceProxy(PaymentService paymentService){
        this.paymentService = paymentService;
    }
    @Override
    public void pay() {
        beforeTime("pay");
        paymentService.pay();
        afterTime("pay");
    }

    /**
     * 方法调用之前执行
     * @param methodName
     */
    public void beforeTime(String methodName){
        startTime = System.currentTimeMillis();
        System.out.println(methodName + "开始执行时间:" + startTime);
    }
    /**
     * 方法调用之后执行
     * @param methodName
     */
    public void afterTime(String methodName){
        System.out.println(methodName + "执行总共用时" + (System.currentTimeMillis() - startTime));
    }
}

这样,每次需要对一个服务进行功能增强,都需要手动为这个服务增加一个属于自己的代理类,这样会导致类的数量增多,增加系统的复杂程度,不易于维护。

2 Jdk动态代理

前面我们知道,静态代理是存在两个问题的:

  1. 新增方法,都需要在代理类中手动实现方法。
  2. 新增服务,需要为每个服务手动增加一个实现类,即使代理类都是为了完成同一同能(如上面都是为了打印方法的执行时间)。

动态代理能够很好的解决这两个问题,目前动态代理有两种方式,JDK动态代理和CGLib动态代理。我们先看看Jdk动态代理是如何使用的:

2.1 jdk动态代理使用

首先,需要实现自己的InvocationHandler,这里的InvocationHandler是对被代理对象和增强功能的封装,jdk动态创建的代理类对象会持有InvocationHandler的引用,具体实现如下:

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 被代理的对象
     */
    private Object object;
    public JdkInvocationHandler(Object object){
        this.object = object;
    }
    /**
     * 这个方法由代理类调用,此时代理类还没有被创建,是在程序执行的时候动态创建的
     * @param proxy 代理类引用,代理类调用此方法的时候传递自身引用this
     * @param method 要执行被代理类的哪个方法,这个是代理类获取被代理类接口中的Method作为参数传来的。
     *               jdk动态创建的代理类和被代理类会实现相同的接口,所以这个Method对象很容易获得
     * @param args 要执行被代理类的方法的参数列表
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前做一些事情,before");
       Object result = method.invoke(object, args);
        System.out.println("调用后做一些事情,after");
        return result;
    }
    
}

InvocationHandler中有一个方法invoke,invoke供代理类调用,有三个参数:

参数1:代理类引用,代理类调用此方法的时候传递自身引用this。

参数2:要执行被代理类的哪个方法,invoke中通过method.invoke(object, args)调用被代理对象object的相应方法。

参数3:要执行方法的参数列表。

动态创建代理对象,以及调用的过程如下:

public class JdkProxyTest {
    public static void main(String[] args) {
        //创建代理对象
        ClassLoader classLoader = JdkProxyTest.class.getClassLoader();
        OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(
                OrderService.class.getClassLoader(),
                OrderServiceImpl.class.getInterfaces(),
                new JdkInvocationHandler(new OrderServiceImpl()));
        orderServiceProxy.createOrder();
    }
}

通过调用Proxy.newProxyInstance()动态创建代理类对象,newProxyInstance有三个参数:

参数1:类加载器,运行的时候动态创建的代理类会通过这个类加载器进行加载。

参数2:被代理类实现的接口列表,将来被代理类也会实现这些接口。

参数3:前面实现的InvocationHandler对象,将来被创建的代理类会持有这个对象的引用,用于调用InvocationHandler中的invoke方法。

2.2 jdk动态代理原理分析

到这里,Jdk动态代理就写好了,jdk动态代理看似神秘,其实原理很简单:

  1. 通过Proxy.newProxyInstance()创建动态创建被代理对象,被创建的代理对象是什么样子的:一是实现了被代理对象所实现的全部接口,并重写了相应的方法(所以需要第二个参数); 二是持有InvocationHandler对象引用(第三个参数)。
  2. 具体调用的时候,首先调用代理类的某个方法。
  3. 代理类的所有的方法中,都调用的是InvocationHandler的invoke方法,告知InvocationHandler的invoke我现在调用的方法是谁,并把方法参数传递给InvocationHandler的invoke。
  4. InvocationHandler的invoke中执行具体的被代理对象的相应方法。并在执行具体被代理对象的方法前对其功能进行加强。

具体调用过程如下:

在这里插入图片描述

为了能更清楚的看清楚jdk动态代理的真实面目,下面,我们将jdk动态生成的代理类class字节码写到文件中,通过反编译工具进行反编译,看看动态生成的代理类类是什么样的。

输出动态代理类class代码如下:

public class DynamicProxyTest {
    public static void main(String[] args) throws Exception{
        byte[] bytes = ProxyGenerator.generateProxyClass("Proxy0", new Class[]{OrderService1.class});
        FileOutputStream os = new FileOutputStream("d:\\install\\jad\\Proxy.class");
        os.write(bytes);
        os.close();
    }
}

对Proxy.class文件进行反编译结果如下:

public final class Proxy0 extends Proxy implements OrderService1 {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void createOrder() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void createOrder(int var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("blog.designpatterns.proxy.OrderService1").getMethod("createOrder");
            m4 = Class.forName("blog.designpatterns.proxy.OrderService1").getMethod("createOrder", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

很清楚就能看到,动态生成的代理类:

  1. 实现了被代理类所实现的接口,并重写了其方法。
  2. 持有InvocationHandler引用。
  3. 调用动态代理类的方法,方法中都是调用了InvocationHandler的invoke方法,告诉InvocationHandler现在要调用的是哪个方法,并且传递自身引用和参数列表。

动态代理能够很好的解决静态代理的两个问题,原因是:

  1. InvocationHandler中持有引用对象是Object,可以代理所有的服务。
  2. 代理类是通过Proxy.newProxyInstance()动态创建的,不需要手动去为每个服务都手动实现一个代理类。
  3. 代理类动态创建,接口中的方法增多,会自动实现所有的方法,不需要手动实现。

jdk动态代理还有两个细节:

  1. 代理类重写的方法被final修饰,说明限制了代理类不能再次被继承(后面讲到的Cglib动态代理不能代理final修饰的方法,所以也限制了被jdk动态代理类方法不能再次被代理)。
  2. 除了代理我们定义的方法,还代理了equals, toString,hashCode。

3 实现自己的动态代理

这里是仿照jdk的动态代理,实现自己的动态代理,旨在更进一步的了解jdk动态代理的原理。

要仿照jdk动态代理实现自己的动态代理,主要分为两步:

  1. 定义自己的InvocationHandler接口。
  2. 动态创建代理类对象。

3.1 定义自己的InvocationHandler接口

和jdk的InvocationHandler一样定义,具体如下:

public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args);
}

3.2 动态创建代理类对象

动态创建代理类对象可以拆分为以下几个步骤:

  1. 代码构建Java程序,可以是文本文件,可以只是字符串,这个组装为一个字符串。
  2. 编译java程序,生成Class byte数组。
  3. 通过构造器加载Class,这里会定义自己的类加载器。
  4. 通过加载的Class,创建代理对象。

四个步骤定义如下:

public class MyProxy {
    public static Object newProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler invocationHandler) throws Exception {
        //1、代码构建Java程序,可以是文本文件,可以只是字符串,这个组装为一个字符串
        String proxyJavaSource =  buildProxyJavaSource(interfaces);
        //2、编译java程序,生成Class byte数组
        byte[] classBytes = compailerJavaSource(proxyJavaSource);
        //3、通过构造器加载Class
        Class proxyClass = loadClass(classLoader, classBytes);
        //4、创建代理对象
        Object proxy = createdProxyInstance(proxyClass, invocationHandler);
        return proxy;
    }
}

下面就具体实现这四个步骤

3.2.1 构建java程序

构建java代码,其实就是通过程序去写程序,过程很简单,只需要注意一些实现细节即可。这里写法很捞,具体实现如下:

/**
     * 代码构建Java程序,可以是文本文件,可以只是字符串,这个组装为一个字符串
     * @param interfaces 被代理类实现的接口
     * @return java源程序字符串
     */
    private static String buildProxyJavaSource(Class[] interfaces) {
        StringBuilder builder = new StringBuilder();
        builder.append("package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;\n");

        //包导入
        builder.append("import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;\n");
        builder.append("import java.lang.reflect.Method;\n");
        for (Class ainterface : interfaces) {
            builder.append("import " + ainterface.getName() + ";\n");
        }

        //实现接口
        builder.append("public class Proxy0 implements ");
        for (Class ainterface : interfaces) {
            builder.append(" " + ainterface.getSimpleName() + ",");
        }
        builder.deleteCharAt(builder.length() - 1);
        builder.append("{\n");


        //方法变量
        int index = 0;
        for (int i = 0; i < interfaces.length; i++) {
            Class anInterface = interfaces[i];
            Method[] declaredMethods = anInterface.getDeclaredMethods();
            for (int j = 0; j < declaredMethods.length; j++) {
                builder.append("private static Method m" + index + ";\n");
                index ++;
            }
        }

        //静态代码块
        index = 0;
        builder.append("static {\n");
        builder.append("try{\n");
        for (int i = 0; i < interfaces.length; i++) {
            Class anInterface = interfaces[i];
            Method[] declaredMethods = anInterface.getDeclaredMethods();
            for (int j = 0; j < declaredMethods.length; j++) {
                Method method = declaredMethods[j];
                builder.append("m" + index + " = Class.forName(\""+anInterface.getName()+"\").getMethod(\""+method.getName()+"\",");
                for (Class<?> parameterType : method.getParameterTypes()) {
                    builder.append(parameterType.getName() + ".class,");
                }
                builder.deleteCharAt(builder.length() - 1);
                builder.append(");\n");
                index ++;
            }
        }
        builder.append("} catch (Exception e){ \n");
        builder.append("throw new RuntimeException(e);\n");
        builder.append("}\n");
        builder.append("}\n");

        //handler和构造函数
        builder.append("private MyInvocationHandler myInvocationHandler;\n");
        builder.append("public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {\n");
        builder.append("this.myInvocationHandler = myInvocationHandler;\n");
        builder.append(" }\n");

        //重写方法
        index = 0;
        for (Class ainterface : interfaces) {
            Method[] declaredMethods = ainterface.getDeclaredMethods();
            for (Method method : declaredMethods){
                builder.append("@Override\n");
                builder.append("public ");
                builder.append(method.getReturnType().getName() + " ");
                builder.append(method.getName() + "( ");
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    builder.append(parameterTypes[i].getName() + " var" + i + ",");
                }
                if (parameterTypes.length > 0){
                    builder.deleteCharAt(builder.length() - 1);
                }
                builder.append("){\n");
                builder.append("try{\n");
                //调用handler的invoke方法

                if (method.getReturnType().getName().equals("void")){
                    builder.append("myInvocationHandler.invoke(this, m" + index+ ",");
                }else {
                    builder.append("Object result = myInvocationHandler.invoke(this, m" + index+ ",");
                }
                if (parameterTypes.length == 0){
                    builder.append("null");
                }else {
                    builder.append("new Object[]{");
                    for (int i = 0; i < parameterTypes.length; i++) {
                        builder.append(" var" + i + ",");
                    }
                    builder.deleteCharAt(builder.length() - 1);
                    builder.append("}");
                }
                builder.append(");\n");
                if (!method.getReturnType().getName().equals("void")){
                    builder.append("return ("+method.getReturnType().getName()+")result;\n");
                }

                builder.append("} catch (Exception e){ \n");
                builder.append("throw new RuntimeException(e);\n");
                builder.append("}\n");
                builder.append("}\n");
                index ++;
            }
        }
        builder.append("}\n");
        return builder.toString();
    }

定义测试OrderService接口如下:

/**
 * 订单服务
 */
public interface OrderService {
    /**
     * 创建订单
     * @throws InterruptedException
     */
    void createOrder();


    /**
     * 查询订单
     */
    String createOrder(int a);
}

执行上面的方法构建代理类java源程序结果如下:

package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;
import java.lang.reflect.Method;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService;
public class Proxy0 implements  OrderService{
    private static Method m0;
    private static Method m1;
    static {
        try{
            m0 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder");
            m1 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder",int.class);
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    private MyInvocationHandler myInvocationHandler;
    public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {
        this.myInvocationHandler = myInvocationHandler;
    }
    @Override
    public void createOrder( ){
        try{
            myInvocationHandler.invoke(this, m0,null);
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    @Override
    public java.lang.String createOrder( int var0){
        try{
            Object result = myInvocationHandler.invoke(this, m1,new Object[]{ var0});
            return (java.lang.String)result;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

3.2.2 编译java程序

编译上面写好的java程序字符串。编译使用jdk提供的tools进行编译,细节不讲,直接上代码:

/**
     * 编译java程序,生成Class byte数组
     * @param proxyJavaSource java源程序
     * @return 编译后的class byte数组
     */
    private static byte[] compailerJavaSource(String proxyJavaSource) throws Exception {
        String name = "Proxy0";
        URI uri = URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension);
        MyJavaFile javafile = new MyJavaFile(uri, JavaFileObject.Kind.SOURCE);
        javafile.setContent(proxyJavaSource);
        List<JavaFileObject> javaFileObjects = Lists.newArrayList(javafile);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        MyJavaFileManager myJavaFileManager =  new MyJavaFileManager(
                compiler.getStandardFileManager(null, null, null));

        JavaCompiler.CompilationTask task = compiler.getTask(
                null, myJavaFileManager, null, null, null, javaFileObjects);
        task.call();
        MyJavaFile javaFile = myJavaFileManager.getByteArrayJavaFileObjects().get(0);
        ByteArrayOutputStream outputStream = (ByteArrayOutputStream) javaFile.openOutputStream();
        return outputStream.toByteArray();
    }

编译过程中需要定义自己的JavaFile和JavaFileManager:

JavaFile:

public class MyJavaFile extends SimpleJavaFileObject {
   private String content;
    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    public MyJavaFile(URI uri, Kind kind) {
        super(uri, kind);
    }
    public void setContent(String content){
        this.content = content;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return content;
    }

    @Override
    public OutputStream openOutputStream() throws IOException {
        return outputStream;
    }
}

JavaFileManager:

public class MyJavaFileManager extends ForwardingJavaFileManager {
    /**
     * Creates a new instance of ForwardingJavaFileManager.
     *
     * @param fileManager delegate to this file manager
     */
    public MyJavaFileManager(JavaFileManager fileManager) {
        super(fileManager);
    }

    private Set<MyJavaFile> javaFiles = new HashSet<>();

    public Set<MyJavaFile> getByteArrayJavaFileObjects() {
        return javaFiles;
    }

    // 有字节码的输出的时候 我们自定义一个JavaFileObject 来接受输出
    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        if (JavaFileObject.Kind.CLASS == kind) {
            MyJavaFile javaFile = new MyJavaFile(URI.create("bytes:///" + className.replace(".", "/") + ".class"), kind);
            javaFiles.add(javaFile);
            return javaFile;
        } else {
            return super.getJavaFileForOutput(location, className, kind, sibling);
        }
    }
}

3.2.3 加载class

将java源程序编译成class字节码数组后,接下来就要对编译好的class进行加载:

/**
     * 通过构造器加载Class
     * @param classLoader 自定义类加载器
     * @param classBytes class byte数组
     * @return Class对象
     */
    private static Class loadClass(MyClassLoader classLoader, byte[] classBytes) throws Exception {
        Class<?> clazz = classLoader.myLoadClass(classBytes);
        return clazz;
    }

这里需要定义自己的类加载器,简单实现一下:

public class MyClassLoader extends ClassLoader {
    public Class<?> myLoadClass(byte[] bytes) throws ClassNotFoundException {
        return defineClass(bytes, 0, bytes.length);
    }
}

3.2.4 创建代理对象

最后就是创建代理类对象了:

/**
     * 创建代理对象
     * @param proxyClass
     * @param invocationHandler
     * @return
     */
    private static Object createdProxyInstance(Class proxyClass, MyInvocationHandler invocationHandler) throws Exception {
        Constructor constructor = proxyClass.getDeclaredConstructor(MyInvocationHandler.class);
        constructor.setAccessible(true);
        Object proxy = constructor.newInstance(invocationHandler);
        return proxy;
    }

3.2.5 测试

到这里就成功实现了自己的动态代理,下面测试一下功能如何:

定义两个测试接口:

/**
 * 订单服务
 */
public interface OrderService {
    /**
     * 创建订单
     * @throws InterruptedException
     */
    void createOrder();


    /**
     * 查询订单
     */
    String createOrder(int a);
}
/**
 * 支付服务
 */
public interface PaymentService {
    /**
     * 支付
     */
    void pay();
}

定义一个实现类,同时实现这两个接口:

/**
 * 订单服务
 */
public class OrderServiceImpl implements OrderService, PaymentService {

    @Override
    public void createOrder() {
        System.out.println("创建订单");
    }

    @Override
    public String createOrder(int a) {
        System.out.println("接受到参数 :a = " + a);
        return "返回结果数据";
    }

    @Override
    public void pay() {
        System.out.println("开始支付了");
    }
}

实现自己的InvocationHandler:

public class MyInvocationHandlerImpl implements MyInvocationHandler {
    private Object object;
    public MyInvocationHandlerImpl(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)  {
        try {
            System.out.println("开始调用了");
            Object invoke = method.invoke(object, args);
            System.out.println("调用结束了");
            return invoke;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

具体测试如下,发现创建动态代理类的过程是不是和Jdk的很类似。

public class MyProxyTest {
    public static void main(String[] args) throws Exception {
        //动态创建自己的代理类
        OrderService orderService = (OrderService) MyProxy.newProxyInstance(
                new MyClassLoader(),
                OrderServiceImpl.class.getInterfaces(),
                new MyInvocationHandlerImpl(new OrderServiceImpl()));
        //调用createOrder
        orderService.createOrder();
        //调用带参数的createOrder
        String result = orderService.createOrder(3);
        System.out.println(result);

        //再次动态创建代理类
        PaymentService paymentService = (PaymentService) MyProxy.newProxyInstance(new MyClassLoader(),
                OrderServiceImpl.class.getInterfaces(), new MyInvocationHandlerImpl(new OrderServiceImpl()));
        //调用pay
        paymentService.pay();
    }
}

测试结果如下:

在这里插入图片描述

结果也符合预期,到这里就完成了自己动态代理类的实现。

4 CGlib动态代理原理分析

4.1 CGlib使用

先看看CgLib动态代理是如何使用的:

首先要实现MethodInterceptor接口

public class CglibPrintTimeProxyInterceptor implements MethodInterceptor {
    /**
     * 
     * @param obj  被代理对象
     * @param method 要执行的方法
     * @param args 要执行的方法参数列表
     * @param proxy 
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("调用前执行");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("调用后执行");
        return result;
    }
}

MethodInterceptor和Jdk动态代理中InvocationHandler相似,但是此处有一个很大的不同的地方,就是此时调用实际的被代理类的方法的时候,使用的是proxy.invokeSuper(obj, args);,很明显是调用的是父类的方法。

创建代理类并调用:

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        //创建代理类对象
        OrderService orderService = (OrderServiceImpl)enhancer.create();
        
        orderService.createOrder();
    }
}

执行结果如下:
在这里插入图片描述

为了清楚的了解Cglib的实现原理,可以添加如下代码,就可以打印出动态代理生成的代理类Class:
在这里插入图片描述

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
                "F:\\document\\me\\java\\cglib_proxy_class");

执行之后,在文件夹中,找到生成动态代理类的地方,发现生成了四个class文件:
[外链图片转存失败(img-Prp4KJTU-1567009203883)(DAB490962E0E47DC9695F13589B1FE7F)]

可以通过执行程序打印类型方式找到生成的代理类class,然后对其反编译:
在这里插入图片描述

发现代理类继承了被代理类,重写代理类中createOrder方法实现如下:

public final void createOrder() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$createOrder$0$Method, CGLIB$emptyArgs, CGLIB$createOrder$0$Proxy);
        } else {
            super.createOrder();
        }
    }

发现,调用代理类的createOrder,实际调用的还是MethodInterceptor的intercept方法,然后在MethodInterceptor的intercept中调用的是父类的方法,也就是被代理类的createOrder方法。

这里,其实只是简单的分析了一下Cglib的原理,其实其粗略的原理就是这样:

  1. 动态创建的代理类,继承了被代理类并实现了其方法
  2. 代理类中的方法实际调用的是MethodInterceptor的intercept方法。
  3. MethodInterceptor的intercept方法中会调用代理类父类的相应方法,父类就是被代理类。

但是CGlib真正的实现,要比这复杂的多,本人也没有做过多的研究, 但是下面简单说一个细节,为了后面 jdk动态代理和CGlib动态代理比较做铺垫。

MethodInterceptor中调用intercept方法的时候,调用的是MethodProxy对象的invokeSuper方法。

在这里插入图片描述

invokeSuper方法实现如下:
在这里插入图片描述

其中fci.f2是FastClass类型:

在这里插入图片描述

其实FastClass也是动态生成的,我们前面打印cglib动态生成的class的时候,不是发现有四个class,其中有两个是FastClass。
在这里插入图片描述

重点来了:这里的两个动态生成的FastClass,只有当调用动态代理类被代理的方法的时候,才会动态生成。如果只是创建代理类而不调用被代理的方法,是不会动态创建的。

测试如下:

public class CglibTest {
    public static void main(String[] args) {

        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
                "F:\\document\\me\\java\\cglib_proxy_class");

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        //创建代理类对象
        OrderService orderService = (OrderServiceImpl)enhancer.create();

//        orderService.createOrder();
    }
}

打印生成的class如下,发现,确实没有生成FastClass类。

[外链图片转存失败(img-Az72oukl-1567009203885)(45113C63958F4C62BD90C20885587E27)]

好了,到这里,cglib的实现分析告一段落,更细节的我也没有再研究,想研究的,可以自行继续研究实现细节。

5 jdk动态代理和CGlib动态代理比较

Jdk动态代理:被代理对象必须实现接口,不能代理没有接口的类。

cglib动态代理:对类本身没有限制,但是cglib有个坑,就是不能代理final方法,因为继承不能重写final方法,这个在使用中要注意。

那实际中我们应该选择jdk还是cglib?

这个需要两个因素,一个是代理本身的局限性,二是代理的性能问题。

代理本身的局限性已经提到了,jdk代理的对象必须要有接口,所以相比,cglib功能更强大。

对于性能问题,因为jdk和cglib都是用到了反射, 是比较耗性能的,所以需要写代码去测试,主要测试两个点,一个是代理类的创建性能,一个是执行方法的性能。

这里使用的jdk版本是1.8, cglib版本是3.2.1,不管是创建对象还是执行方法,我们都会先执行预热一下,原因是前面提到的,Cglib有的动态Class,不是在创建代理类的时候就生成了,而是在方法具体的执行的时候才创建的,所以会导致第一次执行会比较耗时。

5.1 创建性能比较

这里比较创建的性能,是模仿为大量不同的对象创建代理类,而非多次为同一个对象创建不同的代理类,因为多次为同一个对象创建代理类,JDK和CGLib对其会进行优化,不能够看出明显的区别。并且在Spring中,所有的代理类都是代理的,也并不需要重复为同一个类创建多次代理。

这里主要为三个类创建代理,OrderService, PaymentService,InvoiceService三个类进行测试:具体测试代码如下:

public class JdkCglibCreateCompiler {
    public static void main(String[] args) {
        Long jdkStart1 = System.currentTimeMillis();
        OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
                OrderService.class.getClassLoader(),
                OrderServiceImpl.class.getInterfaces(),
                new JdkInvocationHandler(new OrderServiceImpl()));
        Long jdkEnd1 = System.currentTimeMillis();
        System.out.println("JDK创建第一个代理类 : " + (jdkEnd1 - jdkStart1));

        Long jdkStart2 = System.currentTimeMillis();
        PaymentService jdkPaymentServiceProxy = (PaymentService) Proxy.newProxyInstance(
                PaymentService.class.getClassLoader(),
                PaymentServiceImpl.class.getInterfaces(),
                new JdkInvocationHandler(new PaymentServiceImpl()));
        Long jdkEnd2 = System.currentTimeMillis();
        System.out.println("JDK创建第二个代理类 : " + (jdkEnd2 - jdkStart2));

        Long jdkStart3 = System.currentTimeMillis();
        InvoiceService jdkPaymentServiceProxy = (InvoiceService) Proxy.newProxyInstance(
                InvoiceService.class.getClassLoader(),
                InvoiceServiceImpl.class.getInterfaces(),
                new JdkInvocationHandler(new InvoiceServiceImpl()));
        Long jdkEnd3 = System.currentTimeMillis();
        System.out.println("JDK创建第三个代理类 : " + (jdkEnd3 - jdkStart3));


        ///******************Cglib*********************
        Long cglibStart1 = System.currentTimeMillis();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        //创建代理类对象
        OrderService orderService = (OrderServiceImpl)enhancer.create();
        Long cglibEnd1 = System.currentTimeMillis();
        System.out.println("CGLIB创建第一个代理类 : " + (cglibEnd1 - cglibStart1));

        Long cglibStart2 = System.currentTimeMillis();
        enhancer.setSuperclass(PaymentServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        //创建代理类对象
        PaymentService paymentService = (PaymentService)enhancer.create();
        Long cglibEnd2 = System.currentTimeMillis();
        System.out.println("CGLIB创建第二个代理类 : " + (cglibEnd2 - cglibStart2));

        Long cglibStart3 = System.currentTimeMillis();
        enhancer.setSuperclass(InvoiceServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        //创建代理类对象
        InvoiceService invoiceService = (InvoiceService)enhancer.create();
        Long cglibEnd3 = System.currentTimeMillis();
        System.out.println("CGLIB创建第三个代理类 : " + (cglibEnd3 - cglibStart3));
    }
}

因为只有三个测试类,为了模拟创建大量创建不同的对象,我这里上面代码重复执行40次,也就是jdk和cglib各自创建120个对象,得到如下结果:
在这里插入图片描述
从图中能够看出,不管是jdk还是cglib,第一次创建对象都会比较耗时,这里把第一次作为预热,只考虑后两次,也就是各自创建80个对象,对其进行加和,结果是Jdk创建80个对象,需要168ms,平均每个2.1ms, Cglib创建80个对象需要227ms,平均每个2.8ms。
结论是:Jdk1.8创建代理类对象的效率略高于cglib3.2.1

5.2 执行性能

这里采用先预热10次,然后各自循环执行5万次,取执行5万次用时:

public class JdkCglibExecCompiler {
    public static void main(String[] args) {
        OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
                OrderService.class.getClassLoader(),
                OrderServiceImpl.class.getInterfaces(),
                new JdkInvocationHandler(new OrderServiceImpl()));
        //预热
        for (int i = 0; i < 10; i++){
           jdkOrderServiceProxy.createOrder();
        }
        Long jdkStart1 = System.currentTimeMillis();
        for (int i = 0; i < 50000; i++){
            jdkOrderServiceProxy.createOrder();
        }
        Long jdkEnd1 = System.currentTimeMillis();
        System.out.println("jdk创建代理类 : " + (jdkEnd1 - jdkStart1));
        System.out.println((jdkEnd1 - jdkStart1));


        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
        OrderService cglibOrderServiceProxy = (OrderServiceImpl)enhancer.create();
        //预热
        for (int i = 0; i < 10; i++){
            cglibOrderServiceProxy.createOrder();
        }
        Long cglibStart1 = System.currentTimeMillis();
        for (int i = 0; i < 50000; i++){
            cglibOrderServiceProxy.createOrder();
        }
        Long cglibEnd1 = System.currentTimeMillis();
        System.out.println("cglib创建代理类 : " + (cglibEnd1 - cglibStart1));
    }
}

为了防止单次执行5万次不具有普遍性,这里执行10次,取每次执行5万次的时间如下:
在这里插入图片描述
结论:Jdk1.8代理类执行效率略高于cglib3.2.1

最后说一点,Spring中默认使用的是Jdk代理,但是可以再配置文件中配置实用其他代理方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章