动态代理invocationHandler详解

之前对动态代理了解仅仅在于表层,一直觉得高不可攀,今天点开了 Proxy 类,欲知故事如何,需 Read The Source Code,再加上看一些别人的文章,对照着自己对源码的理解,
形成此文,通俗易懂,保你看后对动态代理又有了更加深入的理解 先看一个例子熟悉一下吧 先定义接口,之后我们再看,为什么JDK不能代理类,只能代理接口 public interface AddService { /** * <p>Test method</p> * * @param a number a * @param b number b * @return sum of a and b */ int add(int a, int b); }
实现类 public class AddServiceImpl implements AddService { @Override public int add(int a, int b) { return a + b; } } Handler,继承自InvocationHandler,该接口只有一个方法 invoke,你只需要实现它,
然后利用反射 Object invoke = method.invoke(addService, args); 返回接口return invoke; 其他的你想干什么都行,
当然你也完全改变这个这个实现,返回一些别的啥,坏事也是可以的。getProxy方法里面调用Proxy.newProxyInstance 获取一个代理对象 public class AddServiceHandler implements InvocationHandler { private AddService addService; public AddServiceHandler(AddService addService) { this.addService = addService; } public AddService getProxy() { return (AddService) Proxy.newProxyInstance(addService.getClass().getClassLoader(), addService.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object invoke = method.invoke(addService, args); System.out.println("after"); return invoke; } } 使用,首先获取创建实例对象,然后构造一个Handler,再通过Handler获取Proxy 对象,再调用接口的方法。
我们一切的疑问,可能就在 getProxy,他到底返回了什么东西,能够让我们再调用接口方法的时候,
执行的却是 实现的Service 的方法,并且加了一些其它实现,聪明的你可能会说,这用静态代理依然能够实现,
并且要比动态代理来得简单,为什么还要这样复杂的实现。我现在能想到的是,静态代理的话,
你可能需要为每一个代理接口实现一个代理 Handler,然而 InvocationHandler 的话,
你只需要为类似的请求实现一个Handler,为程序的扩展提供了大大的空间。 @Test public void dynamicProxyTest() { AddService service = new AddServiceImpl(); AddServiceHandler addServiceHandler = new AddServiceHandler(service); AddService proxy = addServiceHandler.getProxy(); Assert.assertEquals(3, proxy.add(1, 2)); }
看到这里,我们有很多疑问 Proxy.newProxyInstance() 返回的是什么东西 invoke 方法到底在哪调用 我们在 target 里面没有看到其它任何附带生成的 class,系统到底是怎么做的呢 那我们就要好好看看这些方法的实现原理 Proxy.newProxyInstance public class Proxy implements java.io.Serializable{} private 的构造方法,里面有一个 InvocationHandler,这个就是我们传入的 Handler,
另外还有一个 proxyClassCache,一个代理类的缓存对象,我暂时不打算展开讲这个东西,还没搞明白,
现在需要记住,这个存放这系统帮我们生成的代理类,用了WeakReference 实现, GC 的时候会被回收。 里面有两个参数传过去,KeyFactory() 先不管,ProxyClassFactory() 这个很重要,我们之后遇到了再说 /** parameter types of a proxy class constructor */ private static final Class<?>[] constructorParams = { InvocationHandler.class }; /** * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); /** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h; /** * Prohibits instantiation. */ private Proxy() { } 为了理解方便,我将一些无关精要的代码剔除,留下最重要的两个方法,getProxyClass0(loader, intfs) 根据loader和intfs 获取代理类,
通过这个方法我们获得一个新的类字节码,这个类是运行时生成的,通过这个代理类,getConstructor 获取构造对象,
调用newInstance创建一个实例对象,newInstance是可以传参的,
只需要调用 constructor 的构造方法参数必须是 InvocationHandler.class,所以我们传的是 this 对象。 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); final Constructor<?> cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[]{h}); } 65535 限制,这个get 比较高级,直接从缓存拿,刚开始看到可能觉得有点纳闷,这个 proxyClassCache 之前遇到过了 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); } get方法首先创建一个 valuesMap,获取subKey,里面比较重要的就是subKeyFactory.apply(key, parameter)
,这个方法会帮我们生成代理类的subKey,另外之后会建立一个Factory,当使用get 的时候,便是真正生成 代理类的时候 public V get(K key, P parameter) { Objects.requireNonNull(parameter); expungeStaleEntries(); Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } } ProxyClassFactory apply 不可不看,首先加载接口,
然后使用ProxyGenerator.generateProxyClass 生成Class 字节码文件,
最后再调用 defineClass0 对其加载后返回关键字,作为key,之后再根据这个key获取到真正的class 对象,
到这里,Proxy类已经生成好,并且加载好了,直接返回,这个类是动态生成的,留在内存的数据 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.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) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } } 那我们可以调用ProxyGenerator.generateProxyClass来看一次下这个生成的类,
把它写到文件里,这个类大概就是这个样子,就是我们通过getProxy获取到的实际类,
之后就简单了,可以清楚的看到里面熟悉的add方法,是通过调用了 invoke 来实现的 public final class $Proxy11 extends Proxy implements Service { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy11(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int add(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("proxy.Service").getMethod("add", Integer.TYPE, Integer.TYPE); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } 至此,大部分逻辑已经搞清楚了,那我们大概知道了为什么这个过程要比直接创建对象要慢,
那是因为他第一次的时候需要动态的去创建字节码,然后进行加载,初始化......虽然有缓存,
但是由于使用了 WeakReference,GC后有可能会被回收,那么就得重新加载,一定程度上会降低效率,
所以一般情况下,我们尽量避免这种动态生成类的方式,而是用在编译时生成类的方式取代,这便是 APT 技术的精髓。 作者:为战而生C 链接:https://www.jianshu.com/p/d0ee1ca57f14 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

上一篇:76、递归求汉诺塔


下一篇:SAP UI5 Web Component的React表格控件用法