手写jdk动态代理_笔记

思路

先上一张图
在这里插入图片描述
对目标对象进行代理,本质其实是底层动态生成一个类加载到 jvm 中,该类将实现原目标对象的所有接口,重写所有方法,通过动态代理扩展功能,其实也可以理解为是通过重写扩展功能,下面就通过这个思路手写一个动态代理

实现

jdk动态生成代理对象步骤

  1. 创建目标对象
  2. 目标对象通过反射获取所有接口
  3. 动态生成Java源代码,实现所有接口
  4. 通过IO流生成Java文件
  5. 使用JavaCompiler类将Java文件编译成class文件
  6. 将生成的新类加载到jvm
  7. 获取该类构造器创建代理对象

MyInvocationHandler接口

package com.cml.proxy;

import java.lang.reflect.Method;

/**
 * @author yys
 * @version 1.0
 */
public interface MyInvocationHandler {
    /**
     * 该方法进行加强原方法,需要被实现
     * @param proxy     生成的代理对象
     * @param method    方法对象
     * @param args      方法需要的参数
     * @return          返回Method执行的返回结果
     * @throws Throwable 抛出所有错误和异常
     */
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

MyClassLoader

package com.cml.proxy;

import java.io.*;
import java.util.Objects;

/**
 * @author yys
 * @version 1.0
 */
public class MyClassLoader extends ClassLoader{
    private final File classPathFile;

    public MyClassLoader(){
        String classPath = Objects.requireNonNull(MyClassLoader.class.getResource("")).getPath();
        this.classPathFile = new File(classPath);
    }

    /**
     * 通过类名查找Class对象,必须重写,原方法默认抛出ClassNotFoundException异常
     * @param name
     * @return
     */
    @Override
    protected Class<?> findClass(String name) {

        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        if(classPathFile  != null){
            File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
            if(classFile.exists()){
                try {
                    //将class文件转为字节数组
                    byte[] bytes = file2ByteArray(classFile);
                    //将字节数组转为Class对象返回
                    return defineClass(className,bytes,0,bytes.length);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * File对象转字节数组
     * @param file
     * @return
     * @throws Exception
     */
    private byte[] file2ByteArray(File file) throws Exception {
        FileInputStream in = new FileInputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte [] buff = new byte[1024];
        int len;
        while ((len = in.read(buff)) != -1){
            out.write(buff,0,len);
        }
        return out.toByteArray();
    }
}

MyProxy

package com.cml.proxy;

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.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author yys
 * @version 1.0
 */
public class MyProxy {
    private static final String LN = "\r\n";

    public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h) {
        try {
            //1.动态生成java源代码,字符串拼接
            String src = generateSrc(interfaces);
            System.out.println(src);
            //2.IO流生成java文件
            String filePath = Objects.requireNonNull(MyProxy.class.getResource("")).getPath();
            File f = new File(filePath + "$MyProxy0.java");
            System.out.println(f);
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();

            //3.编译java文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iterable = manage.getJavaFileObjects(f);

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

            //4.加载到JVM
            Class<?> myProxyClass = classLoader.findClass("$MyProxy0");
            //5.获取动态生成的类的构造器
            Constructor<?> c = myProxyClass.getConstructor(MyInvocationHandler.class);
            //加载完class文件删除生成的java源文件
            f.delete();

            //6.返回构造的代理对象
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 拼接java源代码
     */
    private static String generateSrc(Class<?>[] interfaces) {
        StringBuilder sb = new StringBuilder();
        sb.append(MyProxy.class.getPackage()).append(";").append(LN);
        sb.append("import ").append(interfaces[0].getName()).append(";").append(LN);
        sb.append("import java.lang.reflect.*;" + LN);
        sb.append("public class $MyProxy0 implements ").append(interfaces[0].getName()).append("{").append(LN);
        sb.append("MyInvocationHandler h;" + LN);
        sb.append("public $MyProxy0(MyInvocationHandler h) { " + LN);
        sb.append("this.h = h;");
        sb.append("}" + LN);
        for (Method m : interfaces[0].getMethods()) {
            Class<?>[] params = m.getParameterTypes();
            StringBuilder paramNames = new StringBuilder();
            StringBuilder paramValues = new StringBuilder();
            StringBuilder paramClasses = new StringBuilder();

            for (int i = 0; i < params.length; i++) {
                Class<?> clazz = params[i];
                String type = clazz.getName();
                //参数变量名
                String paramName = "var" + i;
                //参数列表拼接类型和变量名
                paramNames.append(type).append(" ").append(paramName);
                //变量名拼接作为Object数组,作为invoke的参数
                paramValues.append(paramName);
                //拼接参数类型作为Class数组,用于反射查找对应的方法对象
                paramClasses.append(clazz.getName()).append(".class");
                if (i < params.length - 1) {
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }

            sb.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(").append(paramNames).append(") {").append(LN)
                .append("try{" + LN)
                .append("Method m = ").append(interfaces[0].getName()).append(".class.getMethod(\"").append(m.getName()).append("\",new Class[]{").append(paramClasses).append("});").append(LN)
                .append(hasReturnValue(m.getReturnType()) ? "return (" + m.getReturnType().getName() + ")" : "").append("this.h.invoke(this,m,new Object[]{").append(paramValues).append("})").append(";").append(LN)
                .append("}catch(Error er) {throw er; }").append(LN)
                .append("catch(Throwable e){").append(LN)
                .append("throw new UndeclaredThrowableException(e);" + LN)
                .append("}").append(LN)
                .append("}");
        }
        sb.append("}" + LN);
        return sb.toString();
    }


    /**
     * 判断方法是否有返回值
     * @param clazz
     * @return
     */
    private static boolean hasReturnValue(Class<?> clazz) {
        return clazz != void.class;
    }

}

测试

定义接口InterfaceOne

package com.cml.test;

public interface InterfaceOne {
    String show(String s1, String s2);

    String hello(String s1);
}

接口实现类

package com.cml.test;

public class ImplOne implements InterfaceOne {

    @Override
    public String show(String s1, String s2) {
        System.out.println("InterfaceOne >> ImplOne >> show");
        return s1 + s2;
    }

    @Override
    public String hello(String s1) {
        return s1;
    }

}

定义通知类

package com.cml.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 此类做通知
 * @author yys
 *
 */
public class Aspect {
    public void before(){
        System.out.println("前置通知");
    }
    
    public void afterReturning(Object ret){
        System.out.println("后置通知, 返回值为-->" + ret);
    }
    
    public void afterThrowing(Throwable e){
        System.out.println("抛出异常通知 ,异常信息: " + e.getMessage());
    }
    
    public void after(){
        System.out.println("最终通知:finally里面的代码,发生异常也会执行。。。");
    }


    public Object around(Object target, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("环绕通知      前");
        Object ret = method.invoke(target, args);
        System.out.println("环绕通知      后");
        return ret;
    }
}

实现MyInvocationHandler接口

package com.cml.test;

import com.cml.proxy.MyClassLoader;
import com.cml.proxy.MyInvocationHandler;
import com.cml.proxy.MyProxy;

import java.lang.reflect.Method;

/**
 * @author yys
 * @version 1.0
 */
public class MyDynamicProxy implements MyInvocationHandler {
    private Object target;
    /**
     * 通知类
     */
    private Aspect aspect;
    //设置通知类
    public void setAspect(Aspect aspect) {
        this.aspect = aspect;
    }

    /**
     * 此方法用于创建代理对象
     * @param target 传入一个被代理的对象
     * @return 返回代理对象
     */
    public Object createProxyInstance(Object target) {
        this.target = target;
        return MyProxy.newProxyInstance(new MyClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        try {
            aspect.before();
            ret = aspect.around(target, method, args);
            aspect.afterReturning(ret);
        } catch (Exception e) {
            aspect.afterThrowing(e);
        } finally {
            aspect.after();
        }
        return ret;
    }
}

执行

package com.cml.test;

/**
 * @author yys
 * @version 1.0
 */
public class Test {
    public static void main(String[] args) {
        MyDynamicProxy myDynamicProxy = new MyDynamicProxy();
        myDynamicProxy.setAspect(new Aspect());
        InterfaceOne interfaceOne = (InterfaceOne) myDynamicProxy.createProxyInstance(new ImplOne());
        interfaceOne.show("show", "cml");
        System.out.println(interfaceOne.getClass());
        System.out.println(interfaceOne.getClass().getSuperclass());
    }
}

结果

在这里插入图片描述

动态生成的类长这个样子

在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程夜游神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值