读懂这个例子理解动态代理的底层原理

如果你要自己实现一个工具类,实现动态创建代理类,怎么实现?

1、动态生成一个类:通过字符串拼接生成一个类:$Proxy0 ,这个类就是代理类
2、将代理类的字符串形式用流的方式写到磁盘$Proxy0.java文件
3、将$Proxy0.java类编译成$Proxy0.class文件,通过程序动态编译
4、自定义类加载器,加载编译好的 $Proxy0.class 到JVM内存
5、通过加载到内存的$Proxy0类字节码 创建一个代理类的实例并返回实例

以下示例代码自测通过,可以拷贝调试查看效果最佳 

被代理类实现的接口 Person


public interface Person {

    public void makeFriends() throws Throwable;
}

被代理类的增强逻辑,通过EternalMother承载 

public class EternalMother implements EternalInvocationHandler {
    public Son mySon;

    public EternalMother(Son mySon) {
        this.mySon = mySon;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        blindDate();
        mySon.makeFriends();
        appointment();
        return null;
    }

    private void blindDate() {
        System.out.println("EternalMother------------通过相亲认识很多妹子");
    }

    private void appointment() {
        System.out.println("EternalMother------------天天和女朋友约会");
    }
}

 EternalInvocationHandler 模拟 InvocationHandler 定义一个调用接口,后面会用到

package com.eternal.myproxy;

import java.lang.reflect.Method;

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

EternalClassLoader 自定义的类加载器,加载动态创建的代理类 

package com.eternal.myproxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class EternalClassLoader extends ClassLoader {
    private File dir;

    public EternalClassLoader(String path) {
        this.dir = new File(path);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (dir != null) {
            File clazzFile = new File(dir, name + ".class");
            if (clazzFile.exists()) {
                try {
                    FileInputStream fileInputStream = new FileInputStream(clazzFile);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = fileInputStream.read(buffer)) != -1) {
                        byteArrayOutputStream.write(buffer, 0, len);
                        return defineClass(
                                "com.eternal.myproxy." + name
                                , byteArrayOutputStream.toByteArray()
                                , 0
                                , byteArrayOutputStream.size());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return super.findClass(name);
    }
}
EternalProxy 创建代理对象的工具类,jdk实现了一个Proxy,我们现在自己实现一个EternalProxy
package com.eternal.myproxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
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.nio.charset.StandardCharsets;

public class EternalProxy {

    private static String path = EternalProxy.class.getResource("").getPath().replace("build/classes/java/main", "src/main/java");

    private static String className = "$Proxy0";

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          EternalInvocationHandler h) {
        //1、动态生成一个类:通过字符串拼接生成一个类:$Proxy0
        String javaClassStr = createJavaClassStr(interfaces);
        File file = new File(path + "\\" + className + ".java");

        //2、将字符串用流的方式写到磁盘$Proxy0.java文件
        createJavaClassFile(javaClassStr);

        //3、将$Proxy0.java类编译成$Proxy0.class文件
        compile();

        //4、自定义类加载器,加载$Proxy0.class 到JVM内存
        EternalClassLoader eternalClassLoader = new EternalClassLoader(path);

        //5、创建一个代理类的实例并返回实例
        try {
            Class<?> clazzOf$Proxy0 = eternalClassLoader.findClass(className);
            Constructor<?> constructor = clazzOf$Proxy0.getConstructor(EternalInvocationHandler.class);
            Object proxyInstance = constructor.newInstance(h);
            return proxyInstance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void compile() {
        try {
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> javaFileObjects = javaFileManager.getJavaFileObjects(path + "\\" + className + ".java");
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null,
                    javaFileManager,
                    null,
                    null,
                    null,
                    javaFileObjects);
            task.call();
            javaFileManager.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void createJavaClassFile(String javaClassStr) {
        File file = new File(path + "\\" + className + ".java");
        FileWriter fileWriter = null;

        try {
            fileWriter = new FileWriter(file);
            fileWriter.write(javaClassStr);
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static String createJavaClassStr(Class<?>[] interfaces) {
        Method[] methods = interfaces[0].getMethods();
        StringBuilder javaClassStrSB = new StringBuilder();

        javaClassStrSB.append("package com.eternal.myproxy;").append(System.lineSeparator());
        javaClassStrSB.append("import java.lang.reflect.Method;").append(System.lineSeparator());
        javaClassStrSB.append("public class ").append(className).append(" implements ").append(interfaces[0].getName()).append("{")
                .append(System.lineSeparator());


        javaClassStrSB.append(" public EternalInvocationHandler h;").append(System.lineSeparator());
        javaClassStrSB.append("public ").append(className).append("(").append("EternalInvocationHandler h){").append(System.lineSeparator());
        javaClassStrSB.append("this.h=h;").append(System.lineSeparator()).append("}").append(System.lineSeparator());

        javaClassStrSB.append(createMethodStr(methods, interfaces[0]));
        return javaClassStrSB.toString();
    }

    private static String createMethodStr(Method[] methods, Class interfaceOne) {
        StringBuilder javaMethodsStrSB = new StringBuilder();
        for (Method method : methods) {
            javaMethodsStrSB
                    .append("public void ").append(method.getName()).append("() throws Throwable {").append(System.lineSeparator())
                    .append("Method md = ").append(interfaceOne.getName()).append(".class.getMethod(\"").append(method.getName())
                    .append("\",new Class[]{});").append(System.lineSeparator())
                    .append("this.h.invoke(this,md,null);").append(System.lineSeparator()).append("}").append(System.lineSeparator());
        }
        javaMethodsStrSB.append("}");
        return javaMethodsStrSB.toString();
    }
}

测试类:EternalProxyTest

  • 被代理类 Son
  • 被代理类的增强逻辑类 EternalMother类
  • 代理类,通过自定义的EternalProxy工具类动态创建,类名 $Proxy0
package com.eternal.myproxy;

import com.eternal.proxy.Person;
import com.eternal.proxy.Son;
import com.eternal.proxy.Test;

public class EternalProxyTest {


    public static void main(String[] args) throws Throwable {
        EternalMother mother = new EternalMother(new Son());

        Person proxyObject = (Person) EternalProxy.newProxyInstance(
                EternalProxyTest.class.getClassLoader()
                , new Class<?>[]{Person.class}
                , mother);

        proxyObject.makeFriends();

    }


}

测试类执行结果:

JDK动态代理-原理分析 通过 java.lang.reflect.Proxy 类实现动态创建代理类
本文通过手写一个 EternalProxy类,实现 Proxy同样的逻辑,以此来解密动态创建代理类并返回代理实例的底层密码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值