天天看點

JDK動态代理 - 手寫實作動态代理類

如果你要自己實作一個工具類,實作動态建立代理類,怎麼實作?

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動态代理 - 手寫實作動态代理類

JDK動态代理-原理分析 通過 java.lang.reflect.Proxy 類實作動态建立代理類

本文通過手寫一個 EternalProxy類,實作 Proxy同樣的邏輯,以此來解密動态建立代理類并傳回代理執行個體的底層密碼