手写实现JDK动态代理

痴心易碎 提交于 2019-12-22 16:15:13

一 参考

https://www.jianshu.com/p/af271ad4e056

二 代码位置

https://gitee.com/cakin24/javatest/tree/master/src/main/java/GPProxy

三 代码

1 Customer

package GPProxy;

public class Customer implements Person{
    public void findLove(){
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("有6块腹肌");
    }
}

2 CustomMeipo

package GPProxy;

import java.lang.reflect.Method;
public class CustomMeipo implements  GPInvocationHandler {

    //被代理的对象,把引用给保存下来
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;
        Class<?> clazz = target.getClass();

        //下半截,老师深入底层来给大家讲解字节码是如何重组的
        //用来生成一个新的对象(字节码重组来实现)
        return GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke( Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
        method.invoke(this.target,args);
        System.out.println("如果合适的话,就准备办事");
        return  null;
    }
}

3 CustomPorxyTest

package GPProxy;

public class CustomPorxyTest {
    public static void main(String[] args) {
        try {
            Person obj = (Person)new CustomMeipo().getInstance(new Customer());
            System.out.println(obj.getClass());
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4 GPClassLoader

package GPProxy;

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

public class GPClassLoader extends ClassLoader{
    private File classPathFile;
    public GPClassLoader(){
        String classPath = GPClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = GPClassLoader.class.getPackage().getName() + "." + name;

        if(classPathFile != null){
            File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1){
                        out.write(buff,0,len);
                    }
                    return  defineClass(className,out.toByteArray(),0,out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if(null != in){
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }


                    if(out != null){
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

5 GPInvocationHandler

package GPProxy;

import java.lang.reflect.Method;
public interface GPInvocationHandler {
    public Object invoke( Object proxy, Method method, Object[] args) throws Throwable;
}

6 GPProxy

package GPProxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class GPProxy {
    public static final String ln = "\r\n";
    public static Object newProxyInstance(GPClassLoader classLoader,Class<?> [] interfaces,GPInvocationHandler h){
        try {
            //1、动态生成源代码.java文件
            String src = generateSrc(interfaces);

            //2、Java文件输出磁盘
            String filePath = GPProxy.class.getResource("").getPath();
            System.out.println(filePath);
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();

            //3、把生成的.java文件编译成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
            Iterable iterable = manage.getJavaFileObjects(f);

            JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
            task.call();
            manage.close();

            //4、编译生成的.class文件加载到JVM中来
            Class proxyClass =  classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
//          f.delete();

            //5、返回字节码重组以后的新的代理对象
            return c.newInstance(h);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces){
        StringBuffer sb = new StringBuffer();
        sb.append("package GPProxy;" + ln);
//        sb.append("import GPProxy.*;" + ln);
        sb.append("import GPProxy.Person;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("GPInvocationHandler h;" + ln);
        sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
        sb.append("this.h = h;");
        sb.append("}" + ln);
        for (Method m : interfaces[0].getMethods()){
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
            sb.append("this.h.invoke(this,m,null);" + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}");
            sb.append("}");
        }
        sb.append("}" + ln);
        return sb.toString();
    }
}

7 Person

package GPProxy;

public interface Person {
    public void findLove();
}

四 测试结果

/F:/Javatest/target/classes/GPProxy/
F:\Javatest\target\classes\GPProxy\$Proxy0.java:4: 错误: 找不到符号
public class $Proxy0 implements GPProxy.Person{
                                       ^
  符号:   类 Person
  位置: 类 GPProxy
F:\Javatest\target\classes\GPProxy\$Proxy0.java:10: 错误: 找不到符号
Method m = GPProxy.Person.class.getMethod("findLove",new Class[]{});
                  ^
  符号:   类 Person
  位置: 类 GPProxy
2 个错误
class GPProxy.$Proxy0
我是媒婆:我要给你找对象,现在已经拿到你的需求
开始物色
高富帅
身高180cm
有6块腹肌
如果合适的话,就准备办事

五 分析

测试结果有两个错误,但不影响结果,这两个错误主要出现在生成的代码上,生成代码如下:

package GPProxy;

import GPProxy.Person;
import java.lang.reflect.Method;

public class $Proxy0 implements GPProxy.Person {   // GPProxy.Person改为Person可规避这个错误
    GPInvocationHandler h;

    public $Proxy0( GPInvocationHandler h ) {
        this.h = h;
    }

    public void findLove() {
        try {
            // // GPProxy.Person改为Person可规避这个错误
            Method m = GPProxy.Person.class.getMethod("findLove", new Class[]{});
            this.h.invoke(this, m, null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

将GPProxy.Person手动修改为Person可规避错误。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!