动态代理 原理简析(java. 动态编译,动态代理)
动态代理:
1.动态编译 JavaCompiler.CompilationTask 动态编译想理解自己查API文档
2.反射被代理类 主要使用Method.invoke(Object o,Object... args);对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
3.类的加载 URLClassLoader可以加载硬盘任意位置的.java文件。class.getClassLoader只能加载classPath目录下的类。
动态代理可以理解为 动态生成发射代理的类。这其中可以动态增加逻辑操作。比如日志的打印,事物的处理等。spring的AOP操作也是动态代理的。
假设我们有一个接口GrowAble可成长的。
package com.cn; public interface GrowAble { void growUp(); }
一棵小树苗实现了这个接口
package com.cn; public class Tree implements GrowAble { @Override public void growUp() { System.out.println("I am a tree , I'm grow up!"); } }
这时我们想不在不改变源码的情况下想知道树长了多少这个操作?
我们需要一个转换接口。
package com.cn; import java.lang.reflect.Method; public interface InvactionHandle { void invoke(Object o,Method m); }
一个实现接口类。
package com.cn; import java.lang.reflect.Method; import java.util.Random; public class HeightInvactionHandle implements InvactionHandle { @Override public void invoke(Object c, Method m) { try { m.invoke(this.o); System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" ); } catch (Exception e) { e.printStackTrace(); } } private Object o; public HeightInvactionHandle(Object o) { super(); this.o = o; } }
现在最重要的Proxy类了。把上述两个接口接口起来。
package com.cn; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import javax.tools.JavaCompiler.CompilationTask; /** * 动态代理 * @author 灵台方寸小道士 */ public class Proxy { public static Object getNewInstance(Class<?> c,Object object) throws Exception { String path = System.getProperty("user.dir") + File.separator + "mybin" + File.separator + "com" + File.separator + "cn" + File.separator; String fileName = "$Proxy.java"; String nextLine = System.getProperty("line.separator"); // create java File String fileValue = "package com.cn;"+nextLine+ "import com.cn.*;"+nextLine+ "import java.lang.reflect.Method;"+nextLine+ "public class $Proxy implements "+ c.getName() +"{"+nextLine+ " private InvactionHandle h;"+nextLine+ " public $Proxy(InvactionHandle hin)"+ nextLine+ " {"+nextLine+ " this.h = hin;"+nextLine+ " }"+nextLine; Method[] methods = c.getDeclaredMethods(); for (Method m:methods) { fileValue += " public "+ m.getReturnType()+" "+m.getName()+"()"+nextLine+ " {"+nextLine+ " try{ "+nextLine+ //测试方法不带参数 所以new Class<?>[]{}空参数传入 " Method me = "+c.getName()+".class.getDeclaredMethod(\""+m.getName()+"\", new Class<?>[]{});"+nextLine+ " h.invoke(this,me);"+nextLine+ " }catch(Exception e){ "+nextLine+ " e.printStackTrace(); }"+nextLine+ " }"+nextLine; } fileValue +="}"+nextLine; File f = new File(path);//是否存在此目录 if (!f.exists()) f.mkdirs(); FileWriter writer = new FileWriter(new File(f,fileName)); writer.write(fileValue); writer.flush(); writer.close(); System.out.println("*************** create java file over ******************"); // compiler 生成 class文件 调取javac编译 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null,null, null); Iterable<? extends JavaFileObject> in = manager.getJavaFileObjects(path+ fileName); CompilationTask task = compiler.getTask(null, manager, null, null,null, in); task.call(); System.out.println("*************** complier class file over ******************"); // loader 加载class文件 的第一种方法 URLClassLoader可以load任意目录下的类! URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + File.separator + "mybin"+ File.separator) }; URLClassLoader loader = new URLClassLoader(urls); Class<?> d = loader.loadClass("com.cn.$Proxy"); System.out.println("*************** loader class file over ******************"); // newInstance class JVM Constructor<?> con = d.getConstructor(InvactionHandle.class); Object o = con.newInstance(object); // newInstance... /** 加载class文件 的第二种方法 ClassLoader只能load位于classpath(src目录)下的类 Class<?> second = Proxy.class.getClassLoader().loadClass("com.cn.$Proxy"); System.out.println(second.getSimpleName()); */ return o; } }
JavaCompiler 是用于编译生成的java代码。在用URLClassLoader将class文件加载进内存。在实例化。
下面一个测试类 Client
package com.cn; public class Client { public static void main(String[] args) throws Exception { Tree tree = new Tree(); InvactionHandle handle = new HeightInvactionHandle(tree); GrowAble gro = (GrowAble)Proxy.getNewInstance(GrowAble.class, handle); gro.growUp(); System.out.println("测试结束"); } }
运行结果
*************** create java file over ****************** *************** complier class file over ****************** *************** loader class file over ****************** I am a tree , I'm grow up! 这棵树长了2174米!!! 测试结束
现在我们在用JDK来做做
package com.cn; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Random; public class JDKInvocationHandle implements InvocationHandler { private Object o; public JDKInvocationHandle(Object o) { super(); this.o = o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { result = method.invoke(o,args); System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" ); } catch (Exception e) { e.printStackTrace(); } return result; } }
测试类
package com.cn; public class Client2 { public static void main(String[] args) { java.lang.reflect.InvocationHandler h = new JDKInvocationHandle(new Tree()); GrowAble gro = (GrowAble) java.lang.reflect.Proxy.newProxyInstance( GrowAble.class.getClassLoader(), new Class[] { GrowAble.class }, h); gro.growUp(); System.out.println("测试结束"); } }
运行结果
I am a tree , I'm grow up!
这棵树长了726米!!!
测试结束
文章目标 学习java动态代理原理。仅当抛砖引玉作用。
文章原创。转载请注明http://www.cnblogs.com/stay-9527/p/3689266.html
Stay Hungry,Stay Foolish!