手写动态代理--模仿jdk动态代理

为了便于理解jdk动态代理,模仿jdk动态代理的思路,模拟写一个动态代理demo,V1思路如下:

  • 拼接代理对象源码,使用文件流写出$Proxy.java文件
  • 将$Proxy.java动态编译成.class文件
  • $Proxy.class文件动态加载到JVM内存中,产生Class对象
  • 使用反射构造出代理对象
  • 用户调用代理对象实现方法代理

源码如下:

定义接口

package com.ant.myJdkProxy;

/**
 * 接口,模拟有无返回值、有无参数多种情况方法
 */
public interface IndexDao {
    void test();
    void test(String s);
    String testReturn(String s,Integer i);
}

定义实现类,即被代理对象:

package com.ant.myJdkProxy;

/**
 * 接口实现类
 */
public class IndexDaoImpl implements IndexDao{
    @Override
    public void test() {
        System.out.println("test()");
    }

    @Override
    public void test(String s) {
        System.out.println("Test("+s+")");
    }

    @Override
    public String testReturn(String s, Integer i) {
        System.out.println("testReturn("+s+","+i);
        return "hshsh";
    }
}

定义产生代理的工具类:

package com.ant.myJdkProxy;

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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 代理工具类
 * 手动模拟jdk动态代理
 * 步骤:根据传进来的目标代理对象,动态实现代理逻辑的代理java对象文件
 * .java文件-->编译成class文件-->加载至jvm虚拟机内存,称为class对象-->反射创建代理对象-->调用代理方法实现代理
 *
 */
public class ProxyUtil {

    /**
     * 生成java文件,假设生成在D盘下,包名是com.ant
     * @param target 需要被代理的目标对象
     * @param interfc 目标对象实现的接口
     * @return
     */
    public static Object getProxy(Object target,Class interfc) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if(null == target || null == interfc){
            throw new IllegalArgumentException("参数不允许为空");
        }
        if(!interfc.isInterface()){
            throw new IllegalArgumentException("必须是接口");
        }
        //1.生成java源代码字符串
        StringBuilder sb = genJavaStr(target,interfc);
        //2.将源代码写成$Proxy.java至D:/com/ant下
        File dir = new File("D:\\com\\ant");
        if(!dir.exists()){
            dir.mkdirs();
        }
        File file = new File("D:\\com\\ant\\$Proxy.java");
        if(!file.exists()){
            file.createNewFile();
        }

        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(sb.toString());
        fileWriter.flush();
        fileWriter.close();

        //3.将$Proxy.java动态编译成字节码
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(file);
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();

        //4.将class文件动态加载到jvm虚拟机内存中
        URL[] urls = new URL[]{new URL("file:D:\\\\")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class<?> clazz = urlClassLoader.loadClass("com.ant.$Proxy");

        //5.反射创建代理对象
        Constructor<?> constructor = clazz.getConstructor(target.getClass());
        return constructor.newInstance(target);
    }

    /**
     * 生成源代码字符串
     * package com.ant
     * public class $Proxy implement interface{
     *     private Object target;
     *
     *     public $Proxy (Object target){
     *         this.target = target;
     *     }
     *
     *     //重写各个interface中的方法,并且在前后加上逻辑,执行目标对象的方法
     *      @Override
     *      public returnType methosName(args){
     *          System.out.println("before")
     *          returntype result = target.methosName(args);
                System.out.println("after");
     *          return result;
     *      }
     * }
     */

    private static StringBuilder genJavaStr(Object target,Class interfc) {
        String line = "\r\n";
        String tab = "\t";
        StringBuilder sb = new StringBuilder();
        sb
                .append("package com.ant;")
                .append(line)
                .append("public class $Proxy implements ").append(interfc.getName()).append(" {")
                .append(line)
                .append(tab)
                .append("private ").append(target.getClass().getName()).append(" target;").append(line)
                .append(tab).append("public $Proxy(").append(target.getClass().getName()).append(" target").append("){").append(line)
                .append(tab).append(tab).append("this.target = target;").append(line)
                .append(tab).append("}")
                .append(line);
        //方法
        Method[] methods = interfc.getMethods();
        if(null!=methods && methods.length>0){
            for(Method m:methods){
                sb.append(line).append(tab)
                        .append("@Override").append(line)
                        .append(tab)
                        //方法名称
                        .append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(");
                Class<?>[] parameterTypes = m.getParameterTypes();
                //增加方法参数
                String modalityParam = "";
                if(null!=parameterTypes && parameterTypes.length>0){
                    StringBuilder sbu = new StringBuilder();
                    //var1,var2,var3  目标对象方法调用时使用
                    StringBuilder modalitySbu = new StringBuilder();
                    for(int i=0;i<parameterTypes.length;i++){
                        sbu.append(" ").append(parameterTypes[i].getName()).append(" var"+i).append(",");
                        modalitySbu.append("var"+i).append(",");
                    }
                    String temp = sbu.toString();
                    sb.append(temp.substring(0,sbu.lastIndexOf(",")));
                    String tempPa = modalitySbu.toString();
                    modalityParam = tempPa.substring(0,tempPa.lastIndexOf(","));
                }
                sb.append("){").append(line)
                        .append(tab).append(tab)
                        .append("System.out.println(\"方法逻辑处理之前\")").append(";").append(line)
                        .append(tab).append(tab);
                        if(void.class!=m.getReturnType()){
                            sb.append("return ");
                        }
                sb.append("target.").append(m.getName()).append("(").append(modalityParam).append(");").append(line)
                        .append(tab).append("}");
            }
        }
        sb.append(line).append("}");
        return sb;
    }
}

测试主函数:

package com.ant.myJdkProxy;

/**
 * 测试主函数
 */
public class Main {
    public static void main(String[] args){
        IndexDao indexDao = new IndexDaoImpl();
        try {
            IndexDao proxy = (IndexDao)ProxyUtil.getProxy(indexDao, IndexDao.class);
            proxy.test();
            proxy.test("success");
            proxy.testReturn("success",2333);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

到目前为止我们简单的动态代理已经写完,但是V1版本存在的问题:代理逻辑是我们写死的,无法达到用户自行控制代理逻辑,即用户无法通过先执行类似于加锁,执行被代理逻辑,再解锁这种方式控制,总得来说代理逻辑比较死,因此我们继续向下升级我们的动态代理逻辑,思路如下:我们通过定义类似于jdk的InvocationHandler方式定义一个我们自己的接口MyInvocationHandler,当用户执行代理对象的方法时,实际上是执行MyInvocationHandler.invoke方法,在invoke方法,我们将目标对象,目标对象执行的Method对象以及方法参数传给用户,由用户自行调用Method.invoke()方法,同时用户可以在调用invoke方法之前和之后增加自己的自定义逻辑,来实现完全与jdk动态代理相同的灵活性:

目标接口:

package com.ant.myJdkProxy.v2;

/**
 * 接口,模拟有无返回值、有无参数多种情况方法
 */
public interface IndexDao {
    void test() throws Throwable;
    void test(String s);
    String testReturn(String s, Integer i);
}

接口实现类:

package com.ant.myJdkProxy.v2;

/**
 * 接口实现类
 */
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("test()");
    }

    @Override
    public void test(String s) {
        System.out.println("Test("+s+")");
    }

    @Override
    public String testReturn(String s, Integer i) {
        System.out.println("testReturn("+s+","+i);
        return "hshsh";
    }
}

自定义的InvocationHandler接口:

package com.ant.myJdkProxy.v2;

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

public interface MyInvocationHandler {

    /**
     * 模拟jdk InvocationHandler,实际上为了能够让用户自定义代理逻辑,提供给用户的钩子函数,由用户定义代理逻辑与真实方法调用的顺序
     * 当用户获取到代理对象调用方法时,代理对象方法中实际上是调用本方法
     * @param method
     * @param args
     * @return
     */
    Object invoke(Object target,Method method,Object...args) throws InvocationTargetException, IllegalAccessException;

}

用户使用代理时,自行创建的InvocationHandler实现,用于实现代理逻辑:

package com.ant.myJdkProxy.v2;

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

public class IndexInvocationHandler implements MyInvocationHandler {
    @Override
    public Object invoke(Object target, Method method, Object... args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("方法执行开始前");
        Object o = method.invoke(target, args);
        System.out.println("方法执行开始后");
        return o;

    }
}

代理工具类:

package com.ant.myJdkProxy.v2;

import javax.tools.JavaCompiler;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 代理工具类
 * 手动模拟jdk动态代理
 * 步骤:根据传进来的目标代理对象,动态实现代理逻辑的代理java对象文件
 * .java文件-->编译成class文件-->加载至jvm虚拟机内存,称为class对象-->反射创建代理对象-->调用代理方法实现代理
 */
public class ProxyUtil {

    /**
     * 生成java文件,假设生成在D盘下,包名是com.ant
     *
     * @param target  需要被代理的目标对象
     * @param interfc 目标对象实现的接口
     * @return
     */
    public static Object getProxy(Object target, Class interfc, MyInvocationHandler invocationHandler) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if (null == target || null == interfc) {
            throw new IllegalArgumentException("参数不允许为空");
        }
        if (!interfc.isInterface()) {
            throw new IllegalArgumentException("必须是接口");
        }
        //1.生成java源代码字符串
        StringBuilder sb = genJavaStr(target, interfc, invocationHandler);
        //2.将源代码写成$Proxy.java至D:/com/ant下
        File file = createJavaFile(sb.toString());
        //3.将$Proxy.java动态编译成字节码
        compile(file);
        //4.将class文件动态加载到jvm虚拟机内存中
        Class clazz = loadClass();
        //5.反射创建代理对象
        Constructor<?> constructor = clazz.getConstructor(target.getClass(), MyInvocationHandler.class);
        return constructor.newInstance(target,invocationHandler);
    }

    /**
     * 生成源代码字符串
     * package com.ant
     * public class $Proxy implement interface{
     * private Object target;
     * <p>
     * public $Proxy (Object target){
     * this.target = target;
     * }
     * <p>
     * //重写各个interface中的方法,并且在前后加上逻辑,执行目标对象的方法
     *
     * @Override public returnType methosName(args){
     * System.out.println("before")
     * returntype result = target.methosName(args);
     * System.out.println("after");
     * return result;
     * }
     * }
     */

    private static StringBuilder genJavaStr(Object target, Class interfc, MyInvocationHandler invocationHandler) {
        String line = "\r\n";
        String tab = "\t";
        StringBuilder sb = new StringBuilder();
        sb
                .append("package com.ant;")
                .append(line)
                .append("public class $Proxy implements ").append(interfc.getName()).append(" {")
                .append(line)
                .append(tab)
                .append("private ").append(target.getClass().getName()).append(" target;").append(line)
                .append("private ").append("com.ant.myJdkProxy.v2.MyInvocationHandler invocationHandler;").append(line)
                .append(tab).append("public $Proxy(").append(target.getClass().getName()).append(" target").append(",").append("com.ant.myJdkProxy.v2.MyInvocationHandler invocationHandler").append("){").append(line)
                .append(tab).append(tab).append("this.target = target;").append(line)
                .append(tab).append(tab).append("this.invocationHandler = invocationHandler;").append(line)
                .append(tab).append("}")
                .append(line);
        //方法
        Method[] methods = interfc.getMethods();
        if (null != methods && methods.length > 0) {
            for (Method m : methods) {
                sb.append(line).append(tab)
                        .append("@Override").append(line)
                        .append(tab)
                        //方法名称
                        .append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(");
                Class<?>[] parameterTypes = m.getParameterTypes();
                //增加方法参数
                String modalityParam = "";
                String reflectParam = "";
                if (null != parameterTypes && parameterTypes.length > 0) {
                    StringBuilder sbu = new StringBuilder();
                    //var1,var2,var3  目标对象方法调用时使用
                    StringBuilder modalitySbu = new StringBuilder();
                    for (int i = 0; i < parameterTypes.length; i++) {
                        sbu.append(" ").append(parameterTypes[i].getName()).append(" var" + i).append(",");
                        modalitySbu.append("var" + i).append(",");
                        reflectParam = reflectParam + parameterTypes[i].getName() + ".class";
                        reflectParam += ",";
                    }
                    String temp = sbu.toString();
                    sb.append(temp.substring(0, sbu.lastIndexOf(",")));
                    String tempPa = modalitySbu.toString();
                    modalityParam = tempPa.substring(0, tempPa.lastIndexOf(","));
                    reflectParam = reflectParam.substring(0, reflectParam.lastIndexOf(","));
                }
                sb.append("){").append(line)
                        .append(tab).append(tab);
                //方法体
                /*
                try{
                    return (java.lang.String)invocationHandler.invoke(target,target.getDeclaredMethod("test",java.lang.String.class,java.lang.Integer.class),var0,var1,var2);
                }cache(Throwable t){
                    t.printStackTrace();
                }
                    return null;
                 */
                sb.append("try {").append(line).append(tab).append(tab).append(tab);
                if (void.class != m.getReturnType()) {
                    sb.append("return ").append("(").append(m.getReturnType().getName()).append(")");
                }
                sb.append("invocationHandler.invoke(").append("target,")
                        .append("target.getClass().getDeclaredMethod(").append("\"").append(m.getName()).append("\"");
                if (null != reflectParam && reflectParam != "") {
                    sb.append(",").append(reflectParam);
                }
                sb.append(")");
                if (m.getParameterTypes() != null && m.getParameterTypes().length > 0) {
                    sb.append(",").append(modalityParam);
                }
                sb.append(");");
                sb.append(line).append(tab).append("}catch(Throwable t){").append(line)
                        .append(tab).append(tab).append(tab).append("t.printStackTrace();").append(line)
                        .append(tab).append(tab).append("}").append(line);
                if(void.class != m.getReturnType()){
                    sb.append(tab).append(tab).append("return null;");
                }
                sb.append(line).append(tab).append("}");
            }
        }
        sb.append(line).append("}");
        return sb;
    }

    /**
     * 根据源代码,创建java文件
     *
     * @param sourceCode
     * @return
     * @throws IOException
     */
    private static File createJavaFile(String sourceCode) throws IOException {
        File dir = new File("D:\\com\\ant");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file = new File("D:\\com\\ant\\$Proxy.java");
        if (!file.exists()) {
            file.createNewFile();
        }

        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(sourceCode);
        fileWriter.flush();
        fileWriter.close();
        return file;
    }

    /**
     * 将源代码编译成字节码文件
     *
     * @param file
     * @throws IOException
     */
    private static void compile(File file) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(file);
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();
    }

    /**
     * 动态加载class文件到jvm内存中
     *
     * @return
     */
    private static Class loadClass() throws MalformedURLException, ClassNotFoundException {
        URL[] urls = new URL[]{new URL("file:D:\\\\")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class<?> clazz = urlClassLoader.loadClass("com.ant.$Proxy");
        return clazz;
    }
}

测试类:

package com.ant.myJdkProxy.v2;

/**
 * 测试主函数
 */
public class Main {
    public static void main(String[] args){
        IndexDao indexDao = new IndexDaoImpl();
        try {
            IndexDao proxy = (IndexDao) ProxyUtil.getProxy(indexDao, IndexDao.class, new IndexInvocationHandler() );
            proxy.test();
            proxy.test("2333");
            proxy.testReturn("牛逼",2333);
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

我们自己实现的jdk动态代理只是demo级别的,有很多内容都没有考虑到,其次在性能上与远程的jdk动态代理完全无法比,因为我们实现的动态代理涉及到多次IO操作,而jdk动态代理直接产生字节码数组(byte[]),然后直接native方法产生Class对象,通过Class对象反射产生代理对象,速度是我们demo的97倍,测试代码如下:

package com.ant.myJdkProxy.jdk;

import com.ant.myJdkProxy.v2.IndexDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkInvocationHandler implements InvocationHandler {

    private IndexDao indexDao;
    public JdkInvocationHandler(IndexDao indexDao){
        this.indexDao = indexDao;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("jdk before");
        Object result = method.invoke(indexDao,args);
        System.out.println("jdk after");
        return result;

    }
}
package com.ant.myJdkProxy.v2;

import com.ant.myJdkProxy.jdk.JdkInvocationHandler;

import java.lang.reflect.Proxy;

/**
 * 测试主函数
 * 经过测试我们自己写的动态代理执行时间是jdk动态代理的97倍
 */
public class Main {
    public static void main(String[] args){
        IndexDao indexDao = new IndexDaoImpl();
        try {
            //1973982603
            long start1 = System.nanoTime();
            IndexDao proxy = (IndexDao) ProxyUtil.getProxy(indexDao, IndexDao.class, new IndexInvocationHandler() );
            proxy.test();
            proxy.test("2333");
            proxy.testReturn("牛逼",2333);
            System.out.println(System.nanoTime()-start1);

            //20197073
            long start2 = System.nanoTime();
            IndexDao indexDao1 = (IndexDao) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{IndexDao.class}, new JdkInvocationHandler(new IndexDaoImpl()));
            indexDao1.test();
            indexDao1.test("2333");
            indexDao1.testReturn("牛逼",2333);
            System.out.println(System.nanoTime()-start2);

        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值