jdk动态代理
简单的说,代理模式是在目标对象和访问对象之间增加了一层代理对象,所有的访问对象通过代理对象来实现对目标对象的调用。
代理对象和目标对象实现同一个接口,由代理对象来调用目标对象,对于外部来说,代理对象就可以替代目标对象被调用。通过这种方式,代理对象中可以在正常的访问中增加额外的逻辑,比如缓存、权限控制、日志记录等。
但是这种静态代理的模式需要增加额外的代理类的实现,Java 5开始引入了动态代理机制,实现了在运行时动态地创建出代理对象,这其实是一种方法调用的拦截,AOP就是利用了这种模式。
动态代理的使用例子
1
|
<span style= "font-size:16px;" > package com.dynamic.jdk;<br><br> /**<br> * 目标类的接口定义<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */ <br> public interface MyInterface {<br> void doSomething();<br>}<br></span> |
1
|
<span style= "font-size:16px;" > package com.dynamic.jdk;<br><br> /**<br> * 目标类的具体实现<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */ <br> public class MyInterfaceImpl implements MyInterface {<br><br> @Override <br> public void doSomething() {<br> System.out.println( "here is my real operation!" );<br> }<br>}<br></span> |
1
|
<span style= "font-size:16px;" > package com.dynamic.jdk;<br><br> import java.lang.reflect.InvocationHandler;<br> import java.lang.reflect.Method;<br><br> /**<br> * 自定义的InvocationHandler<br> * 封装具体的调用过程<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */ <br> public class MyInvocationHandler implements InvocationHandler {<br> //target是真正执行方法的目标对象<br> private Object target;<br><br> public MyInvocationHandler(Object target) {<br> super();<br> this.target = target;<br> }<br><br> /**<br> * 代理对象调用的方法<br> * @param proxy<br> * @param method<br> * @param args<br> * @return<br> * @throws Throwable<br> */<br> @Override<br> public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br> System.out.println("before target's operation!");<br> Object result = method.invoke(target, args);<br> System.out.println("after target's operation");<br> return result;<br> }<br>}<br></span> |
1
|
<span style= "font-size:16px;" > package com.dynamic.jdk;<br><br> import java.lang.reflect.Proxy;<br><br> /**<br> * 测试类<br> * <p/><br> * Created by Vincent on 12/2/15.<br> */ <br> public class DynamicTest {<br><br> public static void main(String[] args) {<br><br> //生成目标对象<br> MyInterface myInterface = new MyInterfaceImpl();<br><br> //实例化invocationHandler对象,传入目标对象作为target<br> MyInvocationHandler invocationHandler = new MyInvocationHandler(myInterface);<br><br> //调用Proxy的方法生成代理对象<br> MyInterface proxy = (MyInterface)Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),<br> new Class[]{MyInterface.class}, invocationHandler);<br><br> //调用代理对象的方法<br> proxy.doSomething();<br> }<br>}<br></span> |
输出结果:
before target's operation!
here is my real operation!
after target's operation!
使用起来很简单,接下来看源码分析其中的原理
从Proxy的newProxyInstance()方法开始,这个方法就是用来生成代理对象的,需要传入类加载器、实现的接口以及一个InvocationHandler对象。
1
|
<span style= "font-size:16px;" > public static Object newProxyInstance(ClassLoader loader,<br> Class<?>[] interfaces,<br> InvocationHandler h)<br> throws IllegalArgumentException<br>{<br> Objects.requireNonNull(h);<br><br> final Class<?>[] intfs = interfaces.clone();<br> final SecurityManager sm = System.getSecurityManager();<br> if (sm != null ) {<br> checkProxyAccess(Reflection.getCallerClass(), loader, intfs);<br> }<br><br> //通过传入的类加载器和实现接口生成代理类<br> Class<?> cl = getProxyClass0(loader, intfs);<br><br> /*<br> * Invoke its constructor with the designated invocation handler.<br> */<br> try {<br> if (sm != null) {<br> checkNewProxyPermission(Reflection.getCallerClass(), cl);<br> }<br> //反射获取代理类的构造函数<br> final Constructor<?> cons = cl.getConstructor(constructorParams);<br> final InvocationHandler ih = h;<br> if (!Modifier.isPublic(cl.getModifiers())) {<br> AccessController.doPrivileged(new PrivilegedAction<Void>() {<br> public Void run() {<br> cons.setAccessible(true);<br> return null;<br> }<br> });<br> }<br> //反射生成实例,将invocationHandler传入,之后调用invoke方法就靠它了<br> return cons.newInstance(new Object[]{h});<br> } catch (IllegalAccessException|InstantiationException e) {<br> throw new InternalError(e.toString(), e);<br> } catch (InvocationTargetException e) {<br> Throwable t = e.getCause();<br> if (t instanceof RuntimeException) {<br> throw (RuntimeException) t;<br> } else {<br> throw new InternalError(t.toString(), t);<br> }<br> } catch (NoSuchMethodException e) {<br> throw new InternalError(e.toString(), e);<br> }<br>} <br></span> |
getProxyClass0()方法就是用来生成代理类的,首先检查实现接口数量,大于65535就抛异常,记得以前java培训的时候老师讲过这块,感觉应该不会有实现那么多接口的类吧。
1
|
<span style= "font-size:16px;" > private static Class<?> getProxyClass0(ClassLoader loader,<br> Class<?>... interfaces) {<br> if (interfaces.length > 65535 ) {<br> throw new IllegalArgumentException( "interface limit exceeded" );<br> }<br><br> //从一个Cache中获取代理类,如果没有就重新生成<br> return proxyClassCache.get(loader, interfaces);<br>}<br></span> |
proxyClassCache是一个静态的WeakCache,定义在Proxy类里。
WeakCache里有两个factory,一个是subKeyFactory, 是一个映射函数(key, parameter)->sub-key, 另一个是valueFactory,是一个映射函数(key, parameter)->value。WeakCache的get方法需要两个参数,一个是key,另一个是parameter。
具体WeakCache的用法与原理这里不再赘述,请参考源码。
1
|
<span style= "font-size:16px;" > private static final WeakCache<ClassLoader, Class<?>[], Class<?>><br> proxyClassCache = new WeakCache<>( new KeyFactory(), new ProxyClassFactory());<br></span> |
这里传入的KeyFactory和ProxyClassFactory是Proxy类中定义的静态类,分别对应了WeakCache中的subKeyFactory和valueFactory,都实现了BiFunction接口,并实现了apply()方法。ProxyClassFactory的apply()方法里完成的就是生成代理类的逻辑,最关键的是
1
|
<span style= "font-size:16px;" > byte [] proxyClassFile = ProxyGenerator.generateProxyClass(<br> proxyName, interfaces, accessFlags);<br></span> |
上面的方法用来生成代理类的字节码,这个代理类里会有接口的实现方法,在实现的方法中会调用InvocationHandler的invoke()方法,有兴趣的可以将生成的字节码写到本地,用反编译工具打开看一下。
那么ProxyClassFactory的apply()方法是在什么时候被调用的呢,回到WeakCache的get()方法
1
|
<span style= "font-size:16px;" > //生成subkey来获取对应的supplier<br>Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));<br>Supplier<V> supplier = valuesMap.get(subKey);<br>Factory factory = null;<br>//一直循环,直到从supplier中获取到value为止<br>//如果没有supplier就生成factory并加入到缓存中(源码说这是一个install的过程)<br>while (true) {<br> if (supplier != null) {<br> V value = supplier.get();<br> if (value != null) {<br> return value;<br> }<br> }<br> <br> if (factory == null) {<br> //Factory是内部类,实现了Supplier接口,实现了get()方法<br> //实际上在get()方法中调用了valueFactory(也就是ProxyClassFactory)的apply()方法<br> //可以debug进去看<br> factory = new Factory(key, parameter, subKey, valuesMap);<br> }<br><br> if (supplier == null) {<br> supplier = valuesMap.putIfAbsent(subKey, factory);<br> if (supplier == null) {<br> supplier = factory;<br> }<br> } else {<br> if (valuesMap.replace(subKey, supplier, factory)) {<br> supplier = factory;<br> } else {<br> supplier = valuesMap.get(subKey);<br> }<br> }<br>}<br></span> |
好辛苦,终于生成代理类了,回到newProxyInstance方法,有了代理类的Class对象,就可以通过反射生成实例(调用的是带InvocationHandler参数的构造函数),生成实例之后就可以用代理对象了。