前言
在【设计模式】——代理模式(静态)以及【设计模式】——代理模式(动态)中,我们已经基本了解了什么是代理模式,以及什么是静态代理,什么是动态代理。
JDK动态代理以及Cglib动态代理其实底层实现原理都是字节码的重组,不过各自对应的代理场景不同,本文我们重点研究jdk动态代理。
通过前文的了解,我们已经知道在JDK动态代理中是JDK动态的帮我们生成一个名为$Proxy0的代理类,那么,我们本文来纯手写实现JDK动态代理,也就是我们自己生成这个代理类,与JDK动态代理的思想一致,只为简化,细节部分本文并未过多研究。
实现
在前文动态代理实现jdk动态代理中我们有这样一个类的调用关系图:
其中需要引用到jdk的Proxy和InvocationHandler这两个类,这里我们自己简单实现MyProxy.java和MyInvocationHandler.java
由于需要动态生成代理类,那么就需要生成,编译,加载到jvm,因此我们实现了一个简单的类加载器MyClassLoader.java
此时类调用关系图略微变化:
不多说,上代码,大多地方都有比较详细的注释
MyInvocationHandler.java
/** 用于自定义代理逻辑处理 * @author WangZhiJun */ public interface MyInvocationHandler { /** invoke * @param proxy 指被代理的对象 * @param method 要调用的方法 * @param args 方法调用时所需要的参数 * @return Object * @throws Throwable 异常 */ Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
MyProxy.java: 这个类就是JDK动态代理的关键,其中进行了代理类的动态生成:
- 生成源代码
- 将生成的源代码输出到磁盘,保存为.java文件
- 编译源代码,并且生成.class文件
- 将class文件中的内容,动态加载到JVM中来
- 返回被代理后的代理对象
/** 生成代理对象的代码 * @author WangZhiJun */ class MyProxy { private static final String ln = "\r\n"; /** 通过此类为一个或多个接口动态的生成实现类 * @param classLoader 类加载器 * @param interfaces 得到全部的接口 * @param h 得到InvocationHandler接口的子类实例 * @return Object */ static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h){ try{ //1、生成源代码 String proxySrc = generateSrc(interfaces[0]); //2、将生成的源代码输出到磁盘,保存为.java文件 String filePath = MyProxy.class.getResource("").getPath(); File f = new File(filePath + "$Proxy0.java"); FileWriter fw = new FileWriter(f); fw.write(proxySrc); fw.flush(); fw.close(); //3、编译源代码,并且生成.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); Iterable iterable = manager.getJavaFileObjects(f); CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable); task.call(); manager.close(); //4.将class文件中的内容,动态加载到JVM中来 //5.返回被代理后的代理对象 Class proxyClass = classLoader.findClass("$Proxy0"); Constructor c = proxyClass.getConstructor(MyInvocationHandler.class); //这里先不删除生成的$Proxy0.java文件,实际上要删除的 f.delete(); return c.newInstance(h); }catch (Exception e) { e.printStackTrace(); } return null; } /** 生成代理对象$Proxy0的源代码 * @param interfaces 抽象对象 * @return String */ private static String generateSrc(Class<?> interfaces){ StringBuilder src = new StringBuilder(); src.append("package com.wang.proxy.custom.jdk.proxy;" + ln); //引入反射相关的包 src.append("import java.lang.reflect.Method;" + ln); //动态代理类实现被代理接口,在此为Person类 src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln); src.append("MyInvocationHandler h;" + ln); src.append("public $Proxy0(MyInvocationHandler h) {" + ln); src.append("this.h = h;" + ln); src.append("}" + ln); //通过反射获取代理接口的所有方法并激活 for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln); src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}"); return src.toString(); } }
MyClassLoader.java
/**将class重新动态load到JVM * @author WangZhiJun */ public class MyClassLoader extends ClassLoader{ private File baseDir; MyClassLoader(){ String basePath = MyClassLoader.class.getResource("").getPath(); this.baseDir = new File(basePath); } @Override protected Class<?> findClass(String name) { String className = MyClassLoader.class.getPackage().getName() + "." + name; if(baseDir != null){ File classFile = new File(baseDir,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(null != out){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } //先不删除,可以看到class文件内容 //classFile.delete(); } } } return null; } }
这里同样是实现【设计模式】——代理模式(动态)中媒婆的例子,其中Person接口以及Girl类在此不再赘述,MeiPo类因为Proxy以及MyInvocation的变化略微改动:
/** 媒婆 代理类 * @author WangZhiJun */ public class MyMeiPo implements MyInvocationHandler { private Person target; /**获取被代理人的个人资料 * @param target 被代理对象 * @return Object */ Object getInstance(Person target) { this.target = target; Class clazz = target.getClass(); return MyProxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我是媒婆,按照你的要求"); System.out.println("开始进行海选..."); System.out.println("------------"); //调用的时候 method.invoke(this.target, args); System.out.println("------------"); System.out.println("选择结束,如果合适的话,就准备办事"); return null; } }
客户端:
/** * @description: 客户端 * @author: WangZhiJun * @create: 2019-08-22 13:51 **/ public class TestCustomJDKProxy { public static void main(String[] args) { Person person= (Person)new MyMeiPo().getInstance(new Girl()); person.findLove(); } }
运行结果:
此时的结果与上节中我们利用jdk的Proxy和InvocationHanlder是一模一样的!
我们看看这个动态生成的$Proxy0代理类是怎么写的:
public class $Proxy0 implements Person { MyInvocationHandler h; public $Proxy0(MyInvocationHandler var1) { this.h = var1; } public void findLove() { try { Method var1 = Person.class.getMethod("findLove"); this.h.invoke(this, var1, (Object[])null); } catch (Throwable var2) { var2.printStackTrace(); } } }
这就是生成的动态代理类,同样是Person的实现类,同样也实现了findLove方法。
现在可以得知MyProxy.newProxyInstance返回的是动态生成的代理类$Proxy0的对象,也可以称作是Person接口的一个实现类的对象。当调用person.findLove()时,其实是调用$Proxy0.findLove(),然后对照刚刚的类调用关系图,即可调用到被代理对象Girl实例的findLove方法,从而实现了动态代理。
此时我们就简单的实现了JDK的动态代理,不过JDK中实际的实现会更加复杂,更加细节!