动态代理模式主要由四个元素共同构成:
1. 接口,接口中的方法是要真正去实现的
2. 被代理类,实现上述接口,这是真正去执行接口中方法的类
3. 代理类,实现InvocationHandler,帮助被代理类去实现方法
4. 测试用例:
举例详解:
1. 接口:
public interface Person { void searchHouse(); }
2. 被代理类Master,实现上述接口:
public interface Person { void searchHouse(); }
3. 代理类HomeLine,实现InvocationHandler接口
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class HomeLink implements InvocationHandler{ private Person target; public Object getInstance(Person target){ this.target = target; Class clazz = target.getClass(); Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); return obj; } @Override public Object invoke(Object obj, Method method, Object[] args) throws Throwable { System.out.println("我是链家,我帮别人找房子.."); //第一个参数是target,也就是被代理类的对象;第二个参数是方法中的参数 method.invoke(target, args); System.out.println("我是链家,已经找完了.."); return null; } }
4.测试用例:
public class TestSearchHouse { public static void main(String[] args) { Person person = (Person) new HomeLink().getInstance(new Master()); person.searchHouse(); } }
5. 结果:
我是链家,我帮别人找房子.. 我是主人,我要找房子,一室一厅! 我是链家,已经找完了..
动态代理类详细分析:
动态代理的步骤:
1. 首先获得一个被代理对象的引用,
2. 获得该引用的接口
3. 生成一个类,这个类实现了我们给的代理对象所实现的接口
4. 上述类编译生成了.class字节码供JVM使用
5. 调用上述生成的class
对于生成的字节码,我们可以输出都磁盘中:
try { byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class}); FileOutputStream os = new FileOutputStream("D:/$Proxy0.class"); os.write(data); os.flush(); os.close(); } catch (Exception e) { e.printStackTrace(); }
对于生成的$Proxy0.class文件,可以用java反编译工具jd-gui-0.3.3.windows查看,
import com.yding.pattern.proxy.Person; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final void searchHouse() throws { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.yding.pattern.proxy.Person").getMethod("searchHouse", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { } throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }
我们还不仅仅满足于上述的理解,需要更进一步的理解,手写java动态代理的细节过程:
有三个类我们需要自己手写,分别是Proxy, ClassLoader, InvocationHandler
1. MyProxy类,有一个方法,newInstance(ClassLoader loader, Class<?> interfaces,MyInvocationHandler h)
import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class MyProxy { private static String ln = " "; /** * Proxy最重要的方法是newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){ ..} * @param loader * @param interfaces * @param h * @return */ public static Object newProxyInstance(MyClassLoader loader,Class<?>[] interfaces,MyInvocationHandler h){ try { //1. 生成源代码 String proxySrc = generateSrc(interfaces[0]); //2. 将源代码输出到磁盘中 String filePath= MyProxy.class.getResource("").getPath(); File f = new File(filePath+"$Proxy0.java"); FileWriter fw = new FileWriter(f); System.out.println(f.getAbsolutePath()); fw.write(proxySrc); fw.flush(); fw.close(); //3. 编译源代码,并且生成.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); //4. 将class文件中的内容,动态加载到jvm中 Iterable iterable = manager.getJavaFileObjects(f); CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable); task.call(); manager.close(); //5. 返回被代理后的对象 Class proxyClass = loader.findClass("$Proxy0"); Constructor c = proxyClass.getConstructor(MyInvocationHandler.class); f.delete(); return c.newInstance(h); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } private static String generateSrc(Class<?> interfaces) { StringBuffer src = new StringBuffer(); src.append("package com.yding.pattern.proxy.customer;" + ln); src.append("import java.lang.reflect.Method;" + ln); 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(); } }
2. MyClassLoader(),有一个方法,findClass(String className)
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class MyClassLoader extends ClassLoader { private File baseDir; public MyClassLoader() { // TODO Auto-generated constructor stub String baseDir = MyClassLoader.class.getResource("").getPath(); this.baseDir = new java.io.File(baseDir); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String className = MyClassLoader.class.getPackage().getName() + "." + name; if (baseDir != null) { File classFile = new File(baseDir, name.replaceAll("\.", "/") + ".class"); System.out.println("class File : " + classFile); System.out.println(classFile.exists()); 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 (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } classFile.delete(); } } } return null; } }
3. MyInvocationHandler
/** * InvocationHandler * @author Rick * */ public interface MyInvocationHandler { //接口的方法invoke public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
4. 用新定义的类再来执行代码:
public class TestSearchHouse { public static void main(String[] args) { Person person = (Person) new HomeLink().getInstance(new Master()); System.out.println(person.getClass()); person.searchHouse(); } }