为什么要使用代理。
先看一个简单的使用案例
1 import java.lang.reflect.Proxy; 2 3 public class ProxyTest { 4 5 public static void main(String[] args) { 6 //开启保存代理类 7 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 8 9 Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[]{ProxyDemo.class}, new InvocationHandlerDemo()); 10 11 } 12 }
Proxy.newProxyInstance方法传入三个参数,类加载器,需要被代理的接口,代理类处理接口。结果返回的是Object对象。
jdk版本1.7,源码如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } //检查权限 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, interfaces); } //生成代理类,核心是这个方法 Class<?> cl = getProxyClass0(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try {
//获取构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() {
//创建对象 return newInstance(cons, ih); } }); } else {
//创建对象 return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); }
//首先从缓存中获取代理类,如果没有,通过Proxy.ProxyClassFactory类生成代理类并放入缓存,然后返回代理类。 return proxyClassCache.get(loader, interfaces); }
下面分析 Proxy.ProxyClassFactory 类的apply方法:
1 @Override 2 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 3 4 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 5 for (Class<?> intf : interfaces) { 6 /* 7 * 验证代理接口 8 * 9 */ 10 Class<?> interfaceClass = null; 11 try { 12 interfaceClass = Class.forName(intf.getName(), false, loader); 13 } catch (ClassNotFoundException e) { 14 } 15 if (interfaceClass != intf) { 16 throw new IllegalArgumentException( 17 intf + " is not visible from class loader"); 18 } 19 /* 20 * 验证是否是接口 21 * 22 */ 23 if (!interfaceClass.isInterface()) { 24 throw new IllegalArgumentException( 25 interfaceClass.getName() + " is not an interface"); 26 } 27 /* 28 * 验证接口是否有多个 29 */ 30 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { 31 throw new IllegalArgumentException( 32 "repeated interface: " + interfaceClass.getName()); 33 } 34 } 35 36 String proxyPkg = null; // package to define proxy class in 37 38 /* 39 * 40 * 验证同一包下非公共接口,包名是否一致 41 * 42 */ 43 for (Class<?> intf : interfaces) { 44 int flags = intf.getModifiers(); 45 if (!Modifier.isPublic(flags)) { 46 String name = intf.getName(); 47 int n = name.lastIndexOf('.'); 48 String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); 49 if (proxyPkg == null) { 50 proxyPkg = pkg; 51 } else if (!pkg.equals(proxyPkg)) { 52 throw new IllegalArgumentException( 53 "non-public interfaces from different packages"); 54 } 55 } 56 } 57 58 if (proxyPkg == null) { 59 // 如果是pulic接口,采用默认包名 60 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 61 } 62 63 /* 64 * 组装代理类名 65 */ 66 long num = nextUniqueNumber.getAndIncrement(); 67 String proxyName = proxyPkg + proxyClassNamePrefix + num; 68 69 /* 70 * 生成代理类,核心方法是ProxyGenerator 中的 generateClassFile方法。 71 */ 72 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 73 proxyName, interfaces); 74 try { 75 return defineClass0(loader, proxyName, 76 proxyClassFile, 0, proxyClassFile.length); 77 } catch (ClassFormatError e) { 78 /* 79 * A ClassFormatError here means that (barring bugs in the 80 * proxy class generation code) there was some other 81 * invalid aspect of the arguments supplied to the proxy 82 * class creation (such as virtual machine limitations 83 * exceeded). 84 */ 85 throw new IllegalArgumentException(e.toString()); 86 } 87 }
private byte[] generateClassFile() {
//添加hashCode、equals、toString方法,到代理方法集合中 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); int var1; int var3;
//遍历代理接口,将接口中的方法添加到代理方法集合中 for(var1 = 0; var1 < this.interfaces.length; ++var1) { Method[] var2 = this.interfaces[var1].getMethods(); for(var3 = 0; var3 < var2.length; ++var3) { this.addProxyMethod(var2[var3], this.interfaces[var1]); } } Iterator var7 = this.proxyMethods.values().iterator(); //验证代理方法集合中返回类型 List var8; while(var7.hasNext()) { var8 = (List)var7.next(); checkReturnTypes(var8); } Iterator var11; try {
//生成含有InvoicationHandler参数的构造方法,并添加到方法集合中 this.methods.add(this.generateConstructor()); var7 = this.proxyMethods.values().iterator(); //遍历代理方法集合,生成方法,字段,放入到方法集合,字段集合中 while(var7.hasNext()) { var8 = (List)var7.next(); var11 = var8.iterator(); while(var11.hasNext()) { ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next(); this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var4.generateMethod()); } } //生成类构造器,添加到方法中 this.methods.add(this.generateStaticInitializer()); } catch (IOException var6) { throw new InternalError("unexpected I/O Exception"); } if (this.methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); for(var1 = 0; var1 < this.interfaces.length; ++var1) { this.cp.getClass(dotToSlash(this.interfaces[var1].getName())); } this.cp.setReadOnly();
//构造字节输出流,按照class文件格式,顺序输出到字节数组中 ByteArrayOutputStream var9 = new ByteArrayOutputStream(); DataOutputStream var10 = new DataOutputStream(var9); try {
//写入魔数 var10.writeInt(-889275714);
//写入此版本号 var10.writeShort(0);
//写入主版本号 var10.writeShort(49);
//写入常量池 this.cp.write(var10);
//写入访问修饰符 var10.writeShort(49);
//写入类索引 var10.writeShort(this.cp.getClass(dotToSlash(this.className)));
//写入父类索引 var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
//写入接口数量 var10.writeShort(this.interfaces.length); //写入接口 for(var3 = 0; var3 < this.interfaces.length; ++var3) { var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName()))); } //写入字段计数 var10.writeShort(this.fields.size()); var11 = this.fields.iterator(); //写入字段 while(var11.hasNext()) { ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next(); var12.write(var10); } //写入方法计数 var10.writeShort(this.methods.size()); var11 = this.methods.iterator(); //写入方法 while(var11.hasNext()) { ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next(); var13.write(var10); } //写入属性计数 var10.writeShort(0); return var9.toByteArray(); } catch (IOException var5) { throw new InternalError("unexpected I/O Exception"); } } }
从上面源码,可以看出生成的代理类应该包括代理接口中所有方法,Object的hashcode,equals,toString方法,一个含有InvocationHandler参数的实例构造方法,一个类初始方法。下面让我们看下生成的代理类源码:
1 public final class $Proxy0 extends Proxy implements ProxyDemo { 2 private static Method m1; 3 private static Method m3; 4 private static Method m0; 5 private static Method m2; 6 7 public $Proxy0(InvocationHandler var1) throws { 8 super(var1); 9 } 10 11 public final boolean equals(Object var1) throws { 12 try { 13 return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); 14 } catch (RuntimeException | Error var3) { 15 throw var3; 16 } catch (Throwable var4) { 17 throw new UndeclaredThrowableException(var4); 18 } 19 } 20 21 public final void sayHello(String var1) throws { 22 try { 23 super.h.invoke(this, m3, new Object[]{var1}); 24 } catch (RuntimeException | Error var3) { 25 throw var3; 26 } catch (Throwable var4) { 27 throw new UndeclaredThrowableException(var4); 28 } 29 } 30 31 public final int hashCode() throws { 32 try { 33 return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); 34 } catch (RuntimeException | Error var2) { 35 throw var2; 36 } catch (Throwable var3) { 37 throw new UndeclaredThrowableException(var3); 38 } 39 } 40 41 public final String toString() throws { 42 try { 43 return (String)super.h.invoke(this, m2, (Object[])null); 44 } catch (RuntimeException | Error var2) { 45 throw var2; 46 } catch (Throwable var3) { 47 throw new UndeclaredThrowableException(var3); 48 } 49 } 50 51 static { 52 try { 53 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); 54 m3 = Class.forName("com.yishi.invoice.proxy.ProxyDemo").getMethod("sayHello", Class.forName("java.lang.String")); 55 m0 = Class.forName("java.lang.Object").getMethod("hashCode"); 56 m2 = Class.forName("java.lang.Object").getMethod("toString"); 57 } catch (NoSuchMethodException var2) { 58 throw new NoSuchMethodError(var2.getMessage()); 59 } catch (ClassNotFoundException var3) { 60 throw new NoClassDefFoundError(var3.getMessage()); 61 } 62 } 63 }
我们可以看到,调用代理类的sayHello方法,实际上调用的是InvocationHandler.invoke方法。
需要注意:代理类中通过反射获取到代理接口的Method对象,并传入到InvocationHandler.invoke方法中。也就是说invoke方法中需要通过Method发射来调用被代理类的方法。
下面看一个复杂的例子:
包结构如下:
1 import java.io.File; 2 import java.io.IOException; 3 import java.lang.reflect.Modifier; 4 import java.net.URL; 5 import java.util.*; 6 7 public class DaoContainer { 8 9 /** 10 * 存储代理类 11 * key为接口名称 12 * value为代理类 13 */ 14 private static Map<String, Object> daoMap = new HashMap<String, Object>(); 15 16 /** 17 * 接口class对象 18 */ 19 private static List<Class<?>> interfaces = new ArrayList<Class<?>>(); 20 21 /** 22 * 存储实例对象 23 * key 为 接口class对象 24 * value为 实例对象 25 */ 26 private static Map<Class<?>, List<Object>> maps = new HashMap<Class<?>, List<Object>>(); 27 28 /** 29 * 解析包下的接口 30 * @param pkgBase 31 */ 32 public void parseInterface(String pkgBase) { 33 //解析包路径 34 //此处简单处理,支持单路劲 35 String[] p = splitPkg(pkgBase); 36 for (String str:p) { 37 Enumeration<URL> resources = null; 38 try { 39 resources = DaoContainer.class.getClassLoader().getResources(str); 40 while (resources.hasMoreElements()){ 41 URL url = resources.nextElement(); 42 File rootFile = new File(url.getFile()); 43 if (rootFile.exists()) { 44 if (rootFile.isDirectory()) { 45 File[] subFiles = rootFile.listFiles(); 46 for (File f : subFiles) { 47 String interfaceName = pkgBase+ "." + f.getName().substring(0,f.getName().lastIndexOf(".")); 48 System.out.println(interfaceName); 49 try { 50 /** 51 * 可以通过解析class文件,获取类属性,减少不必要的类加载。然后通过判断哪些类是需要加载的 52 * 此处简单处理 53 */ 54 Class<?> cl = Class.forName(interfaceName,false, DaoContainer.class.getClassLoader()); 55 if (Modifier.isInterface(cl.getModifiers())) { 56 interfaces.add(cl); 57 } else if (!Modifier.isInterface(cl.getModifiers()) && !Modifier.isAbstract(cl.getModifiers()) && Modifier.isPublic(cl.getModifiers())) { 58 //如果不是接口,分析父类接口 59 Class<?>[] inter = cl.getInterfaces(); 60 for (Class i : inter ) { 61 if (Modifier.isPublic(i.getModifiers())) { 62 List<Object> objs = maps.get(i); 63 if (objs == null) { 64 objs = new ArrayList<Object>(); 65 maps.put(i, objs); 66 } 67 objs.add(cl.newInstance()); 68 } 69 } 70 } 71 } catch (ClassNotFoundException e) { 72 e.printStackTrace(); 73 } catch (IllegalAccessException e) { 74 e.printStackTrace(); 75 } catch (InstantiationException e) { 76 e.printStackTrace(); 77 } 78 } 79 } 80 } 81 } 82 } catch (IOException e) { 83 e.printStackTrace(); 84 } 85 } 86 } 87 88 /** 89 * 以 ,或者; 作为分隔符 90 * @param pkgBase 91 * @return 92 */ 93 private String[] splitPkg(String pkgBase) { 94 //简单处理 95 pkgBase = pkgBase.replace(".","/"); 96 return new String[]{pkgBase}; 97 } 98 99 /** 100 * 创建代理类 101 */ 102 private void createProxy() { 103 for (Class<?> cl : interfaces) { 104 //创建代理类 105 Object proxy = DaoProxyFactory.proxyInstance(cl); 106 107 daoMap.put(cl.getName(),proxy); 108 } 109 110 Set<Map.Entry<Class<?>, List<Object>>> keys = maps.entrySet(); 111 Iterator<Map.Entry<Class<?>, List<Object>>> iterator = keys.iterator(); 112 113 while (iterator.hasNext()) { 114 Map.Entry<Class<?>, List<Object>> entry = iterator.next(); 115 116 Class<?> key = entry.getKey(); 117 118 List<Object> objs = entry.getValue(); 119 120 for (int i = 0; null != objs && i < objs.size(); i++) { 121 Object o = objs.get(i); 122 Object obj = DaoProxyFactory.proxyInstance(key, o); 123 daoMap.put(o.getClass().getName(), obj); 124 } 125 } 126 127 } 128 129 /** 130 * 搜索包下接口 131 * @param pkgBase 132 */ 133 public void searchPkgBean(String pkgBase) { 134 parseInterface(pkgBase); 135 createProxy(); 136 } 137 138 /** 139 * 通过key获取代理类 140 * @return 141 */ 142 public Object getProxyObjectByKey(String key) { 143 return daoMap.get(key); 144 } 145 146 }
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public class DaoProxyFactory { 6 7 private static CommonSolvDemo commonSolvDemo = new CommonSolvDemo(); 8 9 public DaoProxyFactory() { 10 } 11 12 public DaoProxyFactory(CommonSolvDemo com) { 13 this.commonSolvDemo = com; 14 } 15 16 //代理接口 17 public static Object proxyInstance(Class<?> inter) { 18 Object obj = null; 19 try { 20 obj = Proxy.newProxyInstance(DaoProxyFactory.class.getClassLoader(), new Class[]{inter}, new DaoInvocationHandler()); 21 } catch (Exception e) { 22 e.printStackTrace(); 23 } 24 return obj; 25 } 26 27 //代理实现共同接口的实例 28 public static Object proxyInstance(Class<?> inter, Object obj) { 29 Object res = null; 30 try { 31 res = Proxy.newProxyInstance(DaoProxyFactory.class.getClassLoader(), new Class[]{inter}, new DaoInvocationHandler1(obj)); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 return res; 36 } 37 38 private static class DaoInvocationHandler implements InvocationHandler { 39 40 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 41 42 commonSolvDemo.commonSolvStart(); 43 44 commonSolvDemo.commonSolvEnd(); 45 46 return null; 47 } 48 } 49 50 private static class DaoInvocationHandler1 implements InvocationHandler { 51 52 private Object target; 53 54 public DaoInvocationHandler1(Object target) { 55 this.target = target; 56 } 57 58 public DaoInvocationHandler1() { 59 } 60 61 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 62 commonSolvDemo.commonSolvStart(); 63 method.invoke(target,args); 64 commonSolvDemo.commonSolvEnd(); 65 return null; 66 } 67 } 68 }
1 public class CommonSolvDemo { 2 3 4 public void commonSolvStart() { 5 System.out.println("公共处理开始"); 6 } 7 8 9 public void commonSolvEnd() { 10 System.out.println("公共处理结束"); 11 } 12 13 }
1 import java.lang.reflect.Proxy; 2 3 public class ProxyTest { 4 5 public static void main(String[] args) { 6 // test1(); 7 test2(); 8 } 9 10 public static void test2() { 11 DaoContainer container = new DaoContainer(); 12 container.searchPkgBean("com.yishi.invoice.proxy"); 13 // //这种情况类似mybatis底层的代理生成方式 14 ProxyDemo p = (ProxyDemo) container.getProxyObjectByKey("com.yishi.invoice.proxy.ProxyDemo"); 15 p.sayHello("haha"); 16 17 //代理实现统一接口的实例,类似spring底层代理的一种 18 SqlSession sql = (SqlSession) container.getProxyObjectByKey("com.yishi.invoice.proxy.SqlSessionTemplete"); 19 sql.selectLIst("312","21"); 20 } 21 22 public static void test1() { 23 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 24 25 Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[]{ProxyDemo.class}, new InvocationHandlerDemo()); 26 } 27 28 }
总结下,动态代理底层是字节码层面的技术,利用反射获取到需要代理接口方法,然后根据class字节码标准,生成二进制数据。
回到最开始的问题,为什么要使用代理?当需要对一些类,接口提供统一功能的时候,可以考虑使用代理模式。静态代理采用硬编码的方式实现,其思想本质上是一样的。
spring采用两种代理,cglib与jdk代理。如果是接口的话,采用jdk方式,如果是非接口,采用cglib方式。
后面将分析cglib代理原理,敬请期待。