手写jdk动态代理

之前的文章讲了动态代理的实现原理,这里我们来手写一下jdk的动态代理(支持实现多接口和带参数的方法)。主要原理就是生成一个和实现目标对象相同接口的一个代理对象,我们调用方法的时候实际调用的是生成代理对象的方法。

1.手写InvocationHandler,用来调用代理类的方法

import java.lang.reflect.Method;
public interface JtsInvocationHandler {
    /**
     *
     * @param proxy 被代理对象
     * @param method 调用的方法
     * @param objects 方法的参数
     * @return
     * @throws Throwable
     */
    Object invoke(Object proxy, Method method,Object... objects) throws Throwable;
}

2.手写ClassLoader用来加载动态生成的代理对象

public class JtsClassLoader extends ClassLoader {
    private File classPathFile;

    public JtsClassLoader() {
        String classPath = this.getClass().getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = this.getClass().getPackage().getName() + "." + name;
        if (classPathFile != null) {
            //创建同名的class文件
            File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
            if (classFile.exists()) {//如果class文件已经存在
                FileInputStream fis = null;
                ByteArrayOutputStream out = null;
                try {
                    fis = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ((len = fis.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }
                    return defineClass(className, out.toByteArray(), 0, out.size());//将字节转换为class
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {//关闭流操作
                    try {
                        if (null != out) {
                            out.close();
                        }
                        if (null != fis) {
                            fis.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
        return super.findClass(name);
    }
}

3.手写Proxy,用来生成代理对象(支持jdk1.6及以上的版本)

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;
import java.util.concurrent.atomic.AtomicLong;

public class JtsProxy {

    public static final String ln = "\r\n";

    public static Object getProxyInstance(JtsClassLoader classLoader, Class<?> interfaces[],
                                          JtsInvocationHandler invocationHandler) throws Exception {
        AtomicLong proxyNum = new AtomicLong(0);
        String className = "$Proxy" + proxyNum.getAndIncrement();//存储生成的类名

        //1.动态生成源代码
        String src = generateCode(interfaces, className);
        //2.Java文件输出到磁盘
        String filePath = JtsProxy.class.getResource("").getPath();
        //System.out.println(filePath); //查看Java文件
        File f = new File(filePath + className + ".java");
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        //3.将生成的Java文件编译成.class文件 JavaComiler  since 1.6
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
        Iterable iterable = manager.getJavaFileObjects(f);
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
        task.call();
        manager.close();
        //4.将编译生成的.class文件加载到jvm中
        Class proxyClass = classLoader.findClass(className);
        Constructor constructor = proxyClass.getConstructor(JtsInvocationHandler.class);

        //删除文件
        //f.delete(),这里为了方便看到Java代码就没有删除

        //5.返回字节码重组后的代理对象
        return constructor.newInstance(invocationHandler);
    }

    //生成代理类的源代码
    private static String generateCode(Class<?> interfaces[], String className) {
        StringBuffer sb = new StringBuffer();
        sb.append(JtsProxy.class.getPackage() + ";" + ln)
                .append("import java.lang.reflect.Method;" + ln)
                .append("public class "+className+" implements ");

        for (int i = 0; i < interfaces.length; i++) {//实现多个接口
            Class<?> interf = interfaces[i];
            if (i == 0) {
                sb.append(interf.getName());
            } else {
                sb.append("," + interf.getName());
            }
        }

        sb.append("{" + ln)
                .append("    JtsInvocationHandler h;" + ln + ln)
                .append("    public " + className + "(JtsInvocationHandler h) {" + ln)//构造方法
                .append("        this.h = h;" + ln)
                .append("    }" + ln + ln);

        for (Class interf : interfaces) {
            for (Method m : interf.getMethods()) {
                //判断返回值是否为空
                boolean isVoid = "void".equals(m.getReturnType().getName());
                //方法的所有参数类型
                Class<?>[] parameterTypes = m.getParameterTypes();
                //方法的参数类型集合
                StringBuffer paramTypeStr = new StringBuffer();
                //方法的变量名集合
                StringBuffer paramNameStr = new StringBuffer();
                //存储参数类型+变量名
                StringBuffer args = new StringBuffer();
                for (int i = 0; i < parameterTypes.length; i++) {
                    String parameterName = parameterTypes[i].getName();
                    String arg = "arg" + i;
                    if (i == 0) {
                        paramTypeStr.append(parameterName + ".class");
                        paramNameStr.append(arg);
                        args.append(parameterName + " " + arg);
                    } else {
                        paramTypeStr.append("," + parameterName + ".class");
                        paramNameStr.append("," + arg);
                        args.append("," + parameterName + " " + arg);
                    }
                }

                sb.append("    @Override" + ln)
                  .append("    public " + m.getReturnType().getName() + " " + m.getName() + "(" + args + ") {" + ln)
                  .append("        try{" + ln)
                  .append("            Method m = " + interf.getName() + ".class.getMethod(\"" + m.getName() + "\"");

                if (!"".equals(paramTypeStr.toString())) {
                    sb.append("," + paramTypeStr.toString());
                }
                sb.append(");" + ln + "            ");

                if (!isVoid) {
                    sb.append("return (" + m.getReturnType().getName() + ")");
                }
                //关键所在,就是这里调用了InvocationHandler的invoke方法。
                sb.append("this.h.invoke(this,m");
                if (!"".equals(paramNameStr.toString())) {
                    sb.append("," + paramNameStr);
                }
                sb.append(");" + ln)
                  .append("         }catch(Throwable e){" + ln)
                  .append("             e.printStackTrace();" + ln)
                  .append("         }" + ln);
                if (!isVoid) {
                    sb.append("        return null;" + ln);
                }
                sb.append("    }" + ln + ln);
            }
        }

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

ps: 在实现方法的多个参数类型和参数这里,不知道各种大神有没有更好的写法,欢迎吐槽。这里暂时没做多个接口有相同方法的判断。

4.来测一下效果如何

4.1测试接口1

public interface InterfaceOne {
    void add(int i,int j);
}

4.2测试接口2

public interface InterfaceTwo {

    String getStr(String str1, String str2);
    
    void say();
}

4.3接口的实现

public class ServiceImpl implements InterfaceOne,InterfaceTwo {
    @Override
    public void add(int i,int j) {
        System.out.println(i+j);
    }

    @Override
    public String getStr(String str1,String str2) {
        return str1+"," + str2;
    }

    @Override
    public void say() {
        System.out.println("I can say Hello world");
    }
}

4.4 生成代理对象的工厂

import java.lang.reflect.Method;

public class ProxyFactory {
    
    Object target;
    
    public ProxyFactory(Object target){
        this.target = target;
    }

    public Object getProxyInstance() throws Exception{//获取代理对象实例
        return JtsProxy.getProxyInstance(
                new JtsClassLoader(),
                target.getClass().getInterfaces(),
                new JtsInvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
                        System.out.println("begin transaction");
                        Object returnValue = method.invoke(target, args);
                        System.out.println("commit transaction");
                        return returnValue;
                    }
                }
        );
    }
}

4.5测试主程序

public class AppTest {
    public static void main(String[] args) throws Exception{
        InterfaceOne target = new ServiceImpl();
        InterfaceOne proxy = (InterfaceOne)new ProxyFactory(target).getProxyInstance();
        proxy.add(1,2);

        InterfaceTwo proxy2 = (InterfaceTwo)new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy2.getStr("hello", "world"));
        proxy2.say();
    }
}

首先看下生成的代理类代理:

然后看看运行结果:

总结: 动态代理的做法是动态生成一个代理对象继承和被代理对象一样的接口,然后实现和被代理对象相同的方法,调用的时候是通过调用的InvocationHandler的invoke()方法的实现,在invoke方法里面就可以自定义我们想要做的东西了。  我写的生成代理对象只是生成了接口的方法,没有生成被代理对象原有的方法。jdk的动态代理也生成了被代理对象的原有的方法,包括equals()和hashcode()方法,要写的话写法和上面也是一样的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值