##简介 代理模式 使用ProxyGenerator类生成字节码java
前面的2篇文章都提到一些Java动态代理的东西,接下来咱们就更加细致的来聊一聊Java的动态代理。 Java对于动态代理主要提供了Proxy类,在Proxy类中有一个工厂方法newProxyInstance这基本上就是Java Proxy动态代理的核心了。首先看一下这个方法的前面。编程
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Proxy的这个静态工厂的目的就是生成一个代理类的实例,第1个参数loader指明动态生成的类由哪个类加载器来加载。第2个interfaces表示要代理哪些接口的中的方法。注意:Java动态代理中的最终代理的都是方法。后面会从动态生成的类来讲明这个问题。第3个参数h是一个InvocationHandler接口。缓存
首先,思考一下咱们为何要使用接口?这里先记住一点,很重要的一点,咱们使用接口的重要的一个方面是对变化业务的抽象,咱们知道这个位置有一些重要的事情要作,可是具体怎么作是变化的,须要使用这个接口(或者方法)的客户本身决定(实现)。可是如今客户尚未实现,这个类都没有,没有办法处理依赖关系,怎么办?固然是抽象一个接口,依赖于接口就能够了,客户使用的时候经过面向对象的多态机制,实现接口回调。这就是面向对象很是强调的一点面向接口编程。这里的newProxyInstance所依赖的就是抽象类ClassLoader和接口InvocationHandler。多线程
如今咱们在来看InvocationHandler抽象了什么,InvocationHandler接口中只有一个方法app
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
前面咱们说了Java中的动态代理都是针对于方法的代理,invoke方法抽象的就是代理方法的处理过程。代理模式本质上的东西就是针对一个方法,使用一个代理方法,在代理方法中除了处理被代理的方法,顺便处理一下其余的事情,这也是使用代理模式的目的。invoke就是抽象的这个过程,因此实现InvocationHandler接口要完成的事情就是处理一个逻辑,代理方法要完成一些什么事情,这些由客户实现就能够了。ide
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); try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { 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()); } }
newProxyInstance方法主要就是先生成代理的类的Class,而后获取构造函数把客户端传过来的InvacationHandler注入进去。生成代理类的方法详细的介绍间下面的getProxyClass0方法。后面会细致的经过介绍一下生成的代理类的结构来讲明为何必定要使用要求注入InvacationHandler实例。函数
getProxyClass0这个方法主要的做用是动态生成代理类的字节码,获取字节码的Class。其实这边类之因此比较复杂是由于考虑了缓存和同步的工做,这也是这个方法的职责。由于字节码生成是经过ProxyGenerator.generateProxyClass生成的,文章使用ProxyGenerator类生成字节码有介绍。后面再介绍一下一些细节问题。而获取Class也是经过本地方法defineClass0获取的。 这里之因此介绍这个类是为了帮助理解Proxy动态代理的一些细节、限制以及了解这个方法中一些多线程同步的一些技巧。学习
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //代理的接口最多65535个,这和字节码自己的设计有关 //本身写的类直接实现接口数也不能超过65535个 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } Class<?> proxyClass = null; /* 收集全部接口的名字用作生成代理类缓存的key */ String[] interfaceNames = new String[interfaces.length]; // 去除重复的接口 Set<Class<?>> interfaceSet = new HashSet<>(); //检查每个Class是否是接口,能够不能够被指定的loader加载 //显然,若是接口不能被loader加载,生成的代理类也没有办法加载 for (int i = 0; i < interfaces.length; i++) { //获取全限定名 String interfaceName = interfaces[i].getName(); Class<?> interfaceClass = null; try { //检查指定的类加载器loader可否加载指定接口 interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } //检查Class是不是一个interface if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //检查有没有重复的接口 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); //收集接口的全限定名 interfaceNames[i] = interfaceName; } //全部接口的名字做为cache的key List<String> key = Arrays.asList(interfaceNames); //cache 是全部接口名字与生成的代理类的映射 Map<List<String>, Object> cache; //laderToCache 是一个WeakhashMap这里检查loader对应的cache有没有过时 synchronized (loaderToCache) { cache = loaderToCache.get(loader); if (cache == null) { cache = new HashMap<>(); loaderToCache.put(loader, cache); } } synchronized (cache) { do { Object value = cache.get(key); //WeakReference instanceof Reference //true //null instanceof Reference //false //cache中放了Reference类型和pendingGenerationMarker(Object) if (value instanceof Reference) { proxyClass = (Class<?>) ((Reference) value).get(); } //代理类已经被生成,而且没有被回收 if (proxyClass != null) { return proxyClass; } //代理类正在被其余线程生成 else if (value == pendingGenerationMarker) { try { cache.wait();//等代理类生成完通知 } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { //代理类尚未被生成,先标记一个生成标志 //表示这个代理类由我这个线程生成了,其余线程就不用生成了 cache.put(key, pendingGenerationMarker); //结束循环,去后面执行生成代理类的代码 //而且释放cache的锁 break; } } while (true); } //下面这一块是检查把生成的代理类放到哪个包中 //若是有接口不是public的就应该把生成的代理类放到不是public //的哪个包中,否则代理类没有访问权限 //若是有不是public的接口再2个以上的不一样的包,显然是不合法的 //若是都是public的接口,则使用默认的包"com.sun.proxy" try { String proxyPkg = null; for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // 都是public接口使用 com.sun.proxy 包 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { //生成代理类的名字,像是$Proxy0,$Proxy1,$Proxy2... long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成字节码的类和方法,后面详细介绍生成的类的格式 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { //把字节码转换为Class proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } // private static Map<Class<?>, Void> proxyClasses = //Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>()); //缓存生成的代理,给isProxyClass方法使用,这里proxyClasses之因此要使用Map, //是为了直接使用WeakHashMap,可让生成的代理类能够在合适的时机被回收 proxyClasses.put(proxyClass, null); } finally { synchronized (cache) { //若是生成成功了,就把生成的代理类的弱引用缓存起来 if (proxyClass != null) { cache.put(key, new WeakReference<Class<?>>(proxyClass)); } //若是没有生成成功,移除生成中的标志pendingGenerationMarker //让代理类有从新生成的机会 else { cache.remove(key); } //通知其余等待生成代理类的线程 cache.notifyAll(); } } return proxyClass; }
public final class $Proxy extends Proxy implements UserMapper { private static Method m3; public $Proxy(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final int insert(User paramUser) throws { try { return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m3 = Class.forName("cn.freemethod.dao.mapper.ms.UserMapper").getMethod("insert", new Class[] { Class.forName("cn.freemethod.to.User") }); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
上面这个类是经过this
ProxyGenerator.generateProxyClass( proxyName, interfaces)
生成的,这里为了说明它的结构,只截取了一小部分,更多的内容请参考 使用ProxyGenerator类生成字节码 咱们能够看到生成的类是继承了Proxy,而且实现了UserMapper接口,UserMapper就是方法.net
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
的interfaces参数传递进来的接口,这里只有一个接口,因此只实现了这一个接口。生成的代理类有一个带参数InvocationHandler的构造函数,这个是Proxy类须要的。 生成的代理类为代理的接口中的每个方法生成一个静态方法,经过Class.forName实现的,在类初始化的时候就完成了。 既然实现了接口确定是要实现接口中的方法的,咱们看生成的insert方法
return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue();
就是回调的客户端传进来的InvocationHandler实例h的invoke方法。这样咱们就把代理类的实例,代理的方法和参数都传递进去了。在invoke方法中最多见的使用Method方式是: method.invoke(target, args);target是被代理的实例,这样调用的就是被代理实例上的method方法。
首先,使用代理的目的。就是作一件事情的时候顺便作一些其余的事情,你可能会想我直接在方法中作就能够了嘛,为何非要在代理方法中作呢?这主要考虑到一下公共的方法或者逻辑。好比记录一个方法的执行时间,你固然能够在须要记录时间的方法中写
long start = System.currentTimeMillis(); //doSomething long end = System.currentTimeMillis(); log.info("method name time cost:"+(end-start));
这样的逻辑,可是重复的代码绝对是代码的"坏味道",也增长了工做量,这样若是逻辑比较复杂的话,你修改一处,就会修改n处,仅仅是找这n处代码就是一件痛苦的事情,对吧?更加恐怖的是已经设计好的结构,添加新的公共的逻辑,动态代理绝对是一件利器。 咱们仍是以这个简单的记录时间的逻辑为例,来讲明Proxy代理的流程。首先要代理确定是要代理实例的对吧?这个生成代理实例这种比较复杂的事情Proxy已经帮咱们实现了,提供了一个
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这样的静态工厂方法,方法是提供了,可是参数的咱们本身来,第1个参数loader用来加载生成的代理类,为何非要loader呢?这是但愿客户端保证这个loader能加载第2个参数指定的接口,这样才可以加载实现了这些接口的代理类。第2个参数要代理那些接口(方法),用记录方法执行时间的逻辑就表示,要执行那些方法的执行时间。第3个参数InvocationHandler就是表示公共逻辑,因此得明白要处理什么逻辑,如今是要记录方法的执行时间。这个就好办了,直接实现这个接口重写这个接口的invoke方法就能够了,处理一些相似于下面的逻辑就能够了:
public class TimeCostProxy implements InvocationHandler { //被代理的对象 private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); Object result = method.invoke(target,args); long end = System.currentTimeMillis(); log.info("method name time cost:"+(end-start)); return result; } }
newProxyInstance方法返回的代理对象实现了interfaces,因此彻底能够把newProxyInstance方法返回的对象赋值给一个interface,而后执行interface的逻辑。注意,interfaces中的逻辑才是主要的逻辑,InvocationHandler中只是附加的逻辑。理清楚主次有利于理解Java的Proxy代理。
这里再一次记录一下从代码中咱们知道的Proxy.newProxyInstance一些限制:
最后就是getProxyClass0方法中使用到的缓存和同步技巧的确值得学习。