JDK动态代理

JDK动态代理

public interface Person {
    void doSomething();
}

public class PersonImpl implements Person {
    @Override
    public void doSomething() {
        try {
            Thread.sleep(2000); // 模拟耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSomething");
    }
}

public class JDKProxyPerson implements InvocationHandler {

    // 被代理的对象
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();

        Object result = method.invoke(target, args);

        long end = System.currentTimeMillis();

        System.out.println("总耗时:" + (end - start));
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = (Person) new JDKProxyPerson().getInstance(new PersonImpl());
        person.doSomething();
    }
}

JDK动态代理生成对象的步骤如下:

  1. 获取被代理对象的引用,并且获取它的所有接口,反射获取
  2. JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
  3. 动态生成Java代码
  4. 编译新生成的java代码.class文件
  5. 重新加载到JVM

以上过程就叫字节码重组。JDK中有一组规范,在ClassPath下只要是$开头的.class文件,一般都是自动生成的。

下面将内存中的对象字节码通过文件流输出到一个新的.class文件,然后利用反编译工具查看其源代码。

public static void main(String[] args) {
    byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
    try {
        FileOutputStream fos = new FileOutputStream("E:\\$Proxy0.class");
        fos.write(bytes);
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

运行以上代码,我们能在E盘下找到一个$Proxy0.class文件,使用IDEA直接可以打开查看源代码

import com.lzc.proxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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 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"));
            m3 = Class.forName("com.lzc.proxy.Person").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以发现,$Proxy0继承了Proxy类,同时实现了Person接口,而且重写了doSomething方法。在静态块中利用反射找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用对象的方法。上面这些代码都是JDK帮我们自动生成的。

CGLib动态代理

public class CGLibPerson {
    public void doSomething() {
        try {
            Thread.sleep(2000); // 模拟耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSomething");
    }
}

public class CGLibProxyPerson implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        // 将clazz设置为即将生成的新类的父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();

        Object result = methodProxy.invokeSuper(o, objects);

        long end = System.currentTimeMillis();

        System.out.println("总耗时:" + (end - start));

        return result;
    }
}

public static void main(String[] args) {
        CGLibPerson person = (CGLibPerson) new CGLibProxyPerson().getInstance(CGLibPerson.class);
        person.doSomething();
    }

CGLib代理的对象不需要实现任何 接口,它是通过动态继承目标对象实现动态代理的。

在上面的main方法加上一行代码,将CGLib代理后的.class文件写入磁盘

public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\");
        CGLibPerson person = (CGLibPerson) new CGLibProxyPerson().getInstance(CGLibPerson.class);
        person.doSomething();
    }

对比

  1. JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。
  2. JDK动态代理和CGLib代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib代理实现更加复杂,生成代理类比JDK动态代理效率低
  3. JDK动态代理调用 方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率更快

仿写一个JDK动态代理

Person接口

public interface Person {
    String doSomething(String name, Integer age);
    void hello(String name);
}

Person接口实现类PersonImpl

public class PersonImpl implements Person {
    @Override
    public String doSomething(String name, Integer age) {
        return "name = " + name + ", age = " + age;
    }

    @Override
    public void hello(String name) {
        System.out.println("hello name = " + name);
    }
}

生成动态代理类LzcProxy主要看getInstance方法


import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

public class LzcProxy {

    public static final String PROXY_NAME = "$LzcProxy678";

    public static final String A = "public final class " + PROXY_NAME + " extends Proxy implements %s {\n\t%s\n}";
    public static final String B = "static {\n" +
            "        try {\n" +
            "            %s\n" +
            "        } catch (Exception e) {\n" +
            "            throw new RuntimeException(e);\n" +
            "        }\n" +
            "    }";
    // 有参数
    public static final String GET_METHOD1 = "Class.forName(\"%s\").getMethod(\"%s\", %s);";
    // 无参数
    public static final String GET_METHOD2 = "Class.forName(\"%s\").getMethod(\"%s\");";

    public static final String METHOD = "\tpublic %s %s(%s) {\n" +
            "        %s\n" +
            "    }\n";

    public static String THROWABLE = "try {\n" +
            "            %s\n" +
            "        } catch (Throwable e) {\n" +
            "            throw new RuntimeException(e);\n" +
            "        }";


    public static Object getInstance(Class<?>[] interfaces, InvocationHandler handler) {
        File sourceFile = null;
        try {
            // 生成源代码
            String sourceCode = generateSourceCode(interfaces);
            System.out.println(sourceCode);
            // 将源代码写进文件
            sourceFile = generateSourceFile(sourceCode);
            // 把生成的.java文件编译成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manage.getJavaFileObjects(sourceFile);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
            task.call();
            manage.close();
            // 加载类
            Class<?> lzcProxy0 = Class.forName(LzcProxy.class.getPackage().getName() + "." + PROXY_NAME);
            Constructor<?> constructor = lzcProxy0.getConstructor(InvocationHandler.class);
            return constructor.newInstance(handler);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (sourceFile != null) {
                sourceFile.deleteOnExit();
            }
        }
    }

    private static File generateSourceFile(String sourceCode) throws IOException {
        String filePath = LzcProxy.class.getResource("").getPath();
        File file = new File(filePath + PROXY_NAME + ".java");
        FileWriter fw = new FileWriter(file);
        fw.write(sourceCode);
        fw.flush();
        fw.close();
        return file;
    }

    // 生成源代码字符串
    private static String generateSourceCode(Class<?>[] interfaces) {
        // 需要导入的类
        Set<String> importClassName = new HashSet<>();
        importClassName.add("java.lang.reflect.Proxy");
        importClassName.add("java.lang.reflect.InvocationHandler");
        importClassName.add("java.lang.reflect.Method");
        for (Class clazz : interfaces) {
            importClassName.add(clazz.getName());
        }
        // 开始拼接
        StringBuilder sb = new StringBuilder();
        // 导入当前包
        sb.append("package ").append(LzcProxy.class.getPackage().getName()).append(";\n");
        for (String str : importClassName) {
            sb.append("import " + str + ";\n");
        }
        // 实现类字符串
        String implementsStr = Arrays.stream(interfaces).map(Class::getSimpleName).collect(Collectors.joining(","));
        String content = getContent(interfaces);
        sb.append(String.format(A, implementsStr, content));
        return sb.toString();
    }

    private static String getContent(Class<?>[] interfaces) {
        StringBuilder sb = new StringBuilder();
        // 构造函数
        sb.append("public " + PROXY_NAME + "(InvocationHandler var1) {\n\t\tsuper(var1);\n\t}");
        Map<String, MethodInfo> methodMap = getMethodMap(interfaces);
        // 生成变量
        sb.append("\n").append(getMethodField(methodMap));
        sb.append("\n").append(getMethod(methodMap));
        return sb.toString();
    }
    // 获取方法信息
    private static String getMethod(Map<String, MethodInfo> methodMap) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, MethodInfo> entry : methodMap.entrySet()) {
            MethodInfo methodInfo = entry.getValue();
            Method method = methodInfo.getMethod();
            // 生成方法参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            StringBuilder parameterStr = new StringBuilder("");
            StringBuilder contentStr = new StringBuilder("");

            if (!method.getReturnType().getName().equals("void")) {
                contentStr.append("return ").append("(").append(method.getReturnType().getName()).append(")");
            }

            String[] params = new String[parameterTypes.length];
            if (parameterTypes.length > 0)  {
                int parameterTypeIndex = 0;
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i > 0) {
                        parameterStr.append(",");
                    }
                    params[i] = "var" + parameterTypeIndex++;
                    parameterStr.append(parameterTypes[i].getName()).append(" ").append(params[i]);
                }
            }

            contentStr.append("h.invoke(this, ").append(entry.getKey()).append(",");
            if (params.length == 0) {
                contentStr.append("null");
            } else {
                contentStr.append("new Object[]{").append(Arrays.stream(params).collect(Collectors.joining(","))).append("}");
            }
            contentStr.append(");");

            sb.append(String.format(METHOD, method.getReturnType().getName(), method.getName(), parameterStr, String.format(THROWABLE, contentStr)));
        }
        return sb.toString();
    }

    // 生成变量
    private static String getMethodField(Map<String, MethodInfo> methodMap) {
        StringBuilder sb = new StringBuilder();
        Set<String> methodNameSet = methodMap.keySet();
        for (String methodName : methodNameSet) {
            sb.append("\tprivate static Method " + methodName + " = null;\n");
        }
        sb.append(String.format(B, createMethodField(methodMap)));
        return sb.toString();
    }
    // 方法变量实例化
    private static String createMethodField(Map<String, MethodInfo> methodMap) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, MethodInfo> entry : methodMap.entrySet()) {
            MethodInfo methodInfo = entry.getValue();
            Method method = methodInfo.getMethod();
            Class clazz = methodInfo.getClazz();
            String str = null;
            if (method.getParameterTypes().length == 0) {
                str = String.format(GET_METHOD2, clazz.getName(), method.getName());
            } else {
                String methodFieldList = Arrays.stream(method.getParameterTypes()).map(o -> o.getName() + ".class").collect(Collectors.joining(","));
                str = String.format(GET_METHOD1, clazz.getName(), method.getName(), methodFieldList);
            }
            sb.append(entry.getKey()).append(" = ").append(str).append("\n");
        }
        return sb.toString();
    }

    private static TreeMap<String, MethodInfo> getMethodMap(Class<?>[] interfaces) {
        TreeMap<String, MethodInfo> methodMap = new TreeMap<>();
        int index = 0;
        for (Class clazz : interfaces) {
            for (Method method : clazz.getMethods()) {
                methodMap.put("m" + index++, new MethodInfo(clazz, method));
            }
        }
        return methodMap;
    }
}

class MethodInfo {
    // 类
    private Class clazz;
    // 方法
    private Method method;

    public MethodInfo(Class clazz, Method method) {
        this.clazz = clazz;
        this.method = method;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }
}

LzcProxyPerson实现InvocationHandler

public class LzcProxyPerson implements InvocationHandler {
    // 目标对象
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        return LzcProxy.getInstance(target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println(method.getName() + " 执行总耗时:" + (end - start));
        return result;
    }
}

Main方法进行测试

public class Main {
    public static void main(String[] args) {
        Person person = (Person) new LzcProxyPerson().getInstance(new PersonImpl());
        System.out.println("doSomething result = " + person.doSomething("lzc", 20));
        person.hello("hahaha");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值