手写实现动态代理

    xiaoxiao2023-10-08  145

    一般来说最常见的动态代理是这么写的:

    1,正常版本

    import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy implements InvocationHandler { private Object target; public Object getInstance(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDProxy execute before..."); Object result = method.invoke(target,args); System.out.println("JDProxy execute after..."); return result; } }

    2,改进版本 

    public class ProxyFactory { public static Object getProxyInstance(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* System.out.println(proxy); System.out.println(method); System.out.println(args);*/ System.out.println("日志记录begin"); Object result = method.invoke(target, args); System.out.println("日志记录end"); return result; } }); } }

    首先跟一下Proxy.newProxyInstance

    System.out.println(sonProxy.getClass()); class com.sun.proxy.$Proxy0

    代理类的class为$Proxy,就是意味着 Proxy.newProxyInstance的功能就是产生一个$Proxy,而$Proxy是什么呢?有一个骚操作

    通过反编译工具查看class的源码

    byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class}); FileOutputStream fileOutputStream = new FileOutputStream("E://$Proxy123.class"); fileOutputStream.write($Proxy0s); fileOutputStream.flush(); fileOutputStream.close();

    然后把 $Proxy123.class放到idea里面就能看到内容:

    public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void findLove() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.huffman.pattern.proxy.dynamic_jdk.Person").getMethod("findLove"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }

    可以看出来  Proxy.newProxyInstance具体做的就是生成一个代理类$Proxy,代理类里面克隆了当前类所有方法,而且调用findxxx()方法时其实是调用在定义InvocationHandler.invoke()定义的方法;

    所以我们的手写动态代理的思路为:

    1,直接生成$proxy.java

    2,把$proxy.java编译成$proxy.class

    3,用类加载器把$proxy.class生成class实例

    4,把class实例返回代理对象

    所以重写Proxy的newProxyInstance用代码实现为:

    public static Object newProxyInstance(MyClassLoader classLoader,Class<?>[] interfaces,MyInvocationHandler myInvocationHandler) throws IllegalArgumentException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //1.动态生成java文件源码 String src=generateSrc(interfaces); //2.将java源码生成本地文件 String classPath = MyProxy.class.getResource("").getPath(); File file = new File(classPath + "$Proxy0.java"); FileWriter fileWriter = new FileWriter(file); fileWriter.write(src); fileWriter.flush(); fileWriter.close(); //3.动态编译java源码,生成class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null); Iterable iterable = manage.getJavaFileObjects(file); JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable); task.call(); manage.close(); //4.加载class文件生成Class实例 Class proxyClass = classLoader.findClass("$Proxy0"); Constructor c = proxyClass.getConstructor(MyInvocationHandler.class); // file.delete(); //5.根据类型Class生成代理对象 return c.newInstance(myInvocationHandler); }

    自定义类加载器就是把本地生成的$Proxy0.class加载到jvm

    public class MyClassLoader extends ClassLoader { private File classPathFile; public MyClassLoader(){ String classPath = MyClassLoader.class.getResource("").getPath(); this.classPathFile = new File(classPath); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String className = MyClassLoader.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; } }

    至此,手动实现动态代理就完成了;

    最新回复(0)