不用JDK的动态代理,手写JDK动态代理


在这之前需要知道什么是代理模式和动态代理: https://blog.csdn.net/weixin_45832694/article/details/127269976?spm=1001.2014.3001.5502

手写动态代理步骤

  • 1、代理协助类是通过实现 InvocationHandler 接口创建自己的调用处理器,所以这个接口需要自定义-----就叫MyInvocationHandler
  • 2、newProxyInstance需要传一个类加载器,这个类加载器我们需要自定义-----就叫MyClassLoader
  • 3、代理类是通过调用JDK的Proxy类的newProxyInstance方法获取,所以我们需要自定义Proxy类和实现newProxyInstance方法----这个类就叫MyProxy

自定义MyInvocationHandler 接口

定义即可

/**
 * 自定义InvocationHandler接口
 */
public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

自定义类加载器MyClassLoader

类加载器分类

  • bootstrap ClassLoader – 启动类加载器 负责jdk/jre/目录下的jar加载
  • ext ClassLoader – 扩展类加载器 负责jdk/ext/目录下的jar加载
  • App ClassLoader – 应用程序类加载器(系统类加载器)一般我们用ClassLoader

App ClassLoader实现步骤:继承ClassLoader并覆盖findClass

public class MyClassLoader extends ClassLoader{
    private File classPathFile;


    public MyClassLoader() {//通过构造方法初始化classPathFile
        String file = MyClassLoader.class.getResource("").getPath();//获取MyClassLoader字节码文件的绝对路径

        this.classPathFile = new File(file);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = MyClassLoader.class.getPackage().getName() + "." + name;//获取name这个类的全限定路径
        if (classPathFile != null) {
            //获取name的File
            File classFile = new File(classPathFile + "\\" ,name.replaceAll("\\.","/") + ".class");
            if (classFile.exists()){//文件是否存在,存在就加载到JVM
                FileInputStream fis = null;
                ByteArrayOutputStream bos = null;
                try {
                    fis = new FileInputStream(classFile);
                    byte[] bytes = new byte[4096];
                    bos = new ByteArrayOutputStream();
                    int len;
                    while ((len = fis.read(bytes)) != -1){
                        bos.write(bytes,0,len);
                    }
                    bos.flush();
                    return defineClass(path,bos.toByteArray(),0,bos.size());//加载到JVM,返回Class类型
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (bos != null) {
                        try {
                            bos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

自定义MyProxy类

实现newProxyInstance共五个步骤

  • 1.动态生成一个java的源文件
    在外部写一个静态的类,传入目标接口和协助代理类,通过反射机制动态生成一个java的源文件
  • 2.把生成的源文件保存在磁盘上
  • 3.把源文件编译成class文件
  • 4.把class文件加载到JVM内存中
  • 5.根据加载JVM class文件生成class类,创建class对象
  • 最后返回获取的代理类
/**
 * 自定义的Proxy类,用来获取代理类
 *
 */
public class MyProxy {
    private static final String Ln = "\r\n";

    /**
     * newProxyInstance共五个步骤
     * 1.动态生成一个java的源文件
     * 2.把生成的源文件保存在磁盘上
     * 3.把源文件编译成class文件
     * 4.把class文件加载到JVM内存中
     * 5.根据加载JVM class文件生成class类,创建class对象
     * 最后返回获取的代理类
     * @param Loader 这里自定义的类加载器
     * @param interfaces 目标接口
     * @param h  协助完成代理类的类,一般这个类调用invoke方法
     * @return 返回一个代理类
     */
    public static Object newProxyInstance(MyClassLoader Loader, Class<?>[] interfaces,MyInvocationHandler h) {
        //1.动态生成一个java的源文件
        String code = generateCode(interfaces,h);
        //2.把生成的源文件保存在磁盘上
        String path = MyProxy.class.getResource("").getPath();//获取文件路径
        File file = new File(path + "$Proxy0.java");
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);//根据路径创建一个写对象
            fw.write(code);//把StringBuffer的拼接写到磁盘
            fw.flush();//写一定要刷新
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fw == null) {
                try {
                    fw.close();//关闭资源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //3.把源文件编译成class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//获取java编译器对象
        StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);//获取标准的文件管理器
        Iterable iterable = manager.getJavaFileObjects(file);//通过文件管理器,把文件放进迭代器
        //得到编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
        task.call();//编译
        try {
            manager.close();//关闭文件管理器
        } catch (IOException e) {
            e.printStackTrace();
        }

        //4.把class文件加载到JVM内存中
        //通过类加载器加载,自己写的一个类加载器
        Class clazz = null;
        try {
            clazz = Loader.findClass("$Proxy0");//通过自定义类加载器获取class
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //5.根据加载JVM class文件生成class类,创建class对象
        Constructor constructor = null;
        try {
            constructor = clazz.getConstructor(ProxyClass.class);//获取协助代理的类构造器
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            return  constructor.newInstance(h);//生成代理对象
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据反射机制动态生成源文件
     * @param interfaces 目标接口
     * @param handler  协助代理类
     * @return  返回一个字符串拼接而成的源代码
     */
    public static String generateCode(Class<?>[] interfaces,MyInvocationHandler handler){
        StringBuffer sb = new StringBuffer();
        sb.append("package design.proxy.mode1.myproxy;" + Ln);

        sb.append("import java.lang.reflect.Method;" + Ln);
        //$Proxy0代理类名,interfaces[0].getName()代表目标接口
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + Ln);
        //成员变量   代表代理类的协助类
        //sb.append("public ProxyClass h;" + Ln);
        sb.append("public "+handler.getClass().getName()+" h;" + Ln);
        //构造方法
        //sb.append("public $Proxy0(ProxyClass h) {" + Ln);
        sb.append("public $Proxy0("+handler.getClass().getName()+" h) {" + Ln);
        sb.append("this.h = h;" + Ln);
        sb.append("}" + Ln);
        //遍历目标接口的方法
        for (Method m : interfaces[0].getMethods()){
            //m.getReturnType().getName()方法类型名,m.getName()方法名
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + Ln);

            sb.append("try{" + Ln);
            //通过接口创建Method接口得到方法
            sb.append("Method m=" + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + Ln);
            //实现一个invoke调用,通过for循环调用接口中每个方法
            sb.append("this.h.invoke(this,m,null);" + Ln);

            sb.append("}catch(Throwable e){" + Ln);

            sb.append("e.printStackTrace();" + Ln);
            sb.append("}" + Ln);
            sb.append("}" + Ln);

        }
        sb.append("}" + Ln);
        return sb.toString();
    }
}

测试程序

目标接口

/**
 * 目标接口
 */
public interface TargetClass {
    public void sayHi();
}

目标接口的实现

/**
 * 目标接口的实现
 */
public class TargetClassImpl implements TargetClass {


    @Override
    public void sayHi(){
        System.out.println("Hi,手写动态 proxy");
    }
}

代理协助类

/**
 * ProxyClass是动态代理的一部分,还不是真正的代理类,协助代理类工作
 */
public class ProxyClass implements MyInvocationHandler {
    private Object target;//目标对象

    public ProxyClass() {
    }

    public ProxyClass(Object target) {
        this.target = target;
    }

    /**
     * 该方法在目标的方法被执行的时候,被调用
     * 在调用目标方法时,会先调用invoke方法
     * @param proxy   代理类
     * @param method  目标方法
     * @param args    代表目标方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法调用之前功能增强
        System.out.println("目标方法执行前。。。。。");
        Object result = method.invoke(target,args);//该方法真正调用目标方法
        //方法调用之后功能增强
        System.out.println("目标方法执行后。。。。。");
        return result;
    }
}

Client测试类

public class Client {
    public static void main(String[] args) {
        //不使用代理模式
        design.proxy.mode1.type2.TargetClass targetClass = new design.proxy.mode1.type2.TargetClassImpl();
        targetClass.sayHi();

        System.out.println("------------------------------");
        //使用动态代理
        /**
         * 该方法三个参数
         * 第一个:为定义代理类的类加载器,这里可以使用当前类的加载器,或者使用接口的类加载器,这里使用自定义代理类
         * 第二个:代理类要实现的接口列表,是一个数组。
         * 第三个:MyInvocationHandler,将方法调用分派到的调用处理程序,需要把实现类传给协助代理类的类调用invoke方法里
         *        也就是说要传一个实现类给协助类,在协助类里有一个目标成员通过构造方法传参。
         * 返回结果是真正的一个代理类Object类型,该代理类由指定的类加载器定义并实现指定的接口
         */
        TargetClass targetClass1 = (TargetClass) MyProxy.newProxyInstance(new MyClassLoader(),
        new Class<?>[]{TargetClass.class}, new ProxyClass(new TargetClassImpl()));
        targetClass1.sayHi();
    }
}

结果

请添加图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆亦何为

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值