动态代理
代理模式一般用于增强类的方法而不直接修改原始类的代码,动态代理可以用于动态生成目标类的代理类来执行相应操作,目前动态代理主要有JDK动态代理以及CGLIB,经常会提起,也会看一下解释,但是并没有真正实践过,现在学习一下。
JDK动态代理
使用反射机制,一般用来代理接口类。
具体来说
1.要有目标接口
public interface UserService { int hello(String name); }
目标接口实现类
public class UserServiceImpl implements UserService{ @Override public int hello(String name) { System.out.println("hello " + name); return 100; } }
UserServiceImpl类实现UserService接口hello方法,实现一定的业务逻辑,现在假定需要改变或者增强hello方法,一种方法是重新定义一个类实现UserService接口,如果hello方法需要频繁变动则该种方式不合适,可以考虑动态代理的方式,生成一个代理类,代理类中可以调用UserServiceImpl实现逻辑,并且可以增强hello方法。
2.实现InvocationHandler,进行目标方法的增强,调用Proxy.newProxyInstance, 产生动态代理类,执行方法
UserService us = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), (proxy1, method, args1) -> { if(method.getName().equals("hello")){ System.out.println("before"); method.invoke(us, args1); System.out.println("after"); } return 200; }); //us.hello("lisi"); proxy.hello("lisi");
代理类由Proxy.newProxyInstance来产生,其方法主要说明如下
/** * 返回一个实现特定接口的代理类,在代理类中可以根据特定的处理器来转发方法调用 * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. * 类加载器用来加载代理类,一般与目标接口的加载器相同即可 * @param loader the class loader to define the proxy class * 目标接口集合 * @param interfaces the list of interfaces for the proxy class * to implement * 转发方法调用,再次实现代理方法的增强 * @param h the invocation handler to dispatch method invocations to * @return a proxy instance with the specified invocation handler of a * proxy class that is defined by the specified class loader * and that implements the specified interfaces */
其实质是对于目标类来说,经过编译成class文件由类加载器进行加载执行,对于代理类,根据目标类和InvocationHandler来动态组装class文件,实现目标类的方法增强。
InvocationHandler方法的定义
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
其中invoke方法参数说明如下:
proxy代理类实例,method代理类调用的方法,args代理类调用方法传入的参数
/** * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. * * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * {@code null} and the interface method's return type is * primitive, then a {@code NullPointerException} will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a {@code ClassCastException} will be thrown by the method * invocation on the proxy instance. */
JDK动态代理源码分析
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法中核心逻辑
生成代理类class文件
/* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs);
根据class文件获取构造函数,构造代理类实例并返回,这里使用反射原理
生成的class文件中含有InvocationHandler为参数的构造函数,调用该构造函数,产生代理类实例。
final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; return cons.newInstance(new Object[]{h});
这里的核心是代理类class文件生成过程
代理类class生成
/* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs);
该方法传入类加载器以及接口集合,产生class文件。可知代理类class数据会被缓存,初次代理会产生class数据,再次使用代理会根据类加载器和接口来从缓存中获取。
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */ 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); }
WeakCache用来缓存代理class数据
/** * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
Cache mapping pairs of {@code (key, sub-key) -> value}. Keys and values are weakly but sub-keys are strongly referenced.
Keys and values are weakly but sub-keys are strongly referenced.
可知WeakCache实现了双关键字和值得对应缓存。在此处key=类加载器,subKey=接口集合,判断两个类是同一个类需要满足两个条件,class相同并且类加载器相同,主关键词对于值是弱相关,即主关键字相同,值未必相同,副关键词对于值是强相关,即副关键词不相同则值必定不相同。
new KeyFactory()通过对于类加载器和接口集合来产生key;new ProxyClassFactory()产生代理class数据
KeyFactory实现BiFunction接口,BiFunction<ClassLoader, Class<?>[], Object>表示传入ClassLoader和Class<?>[]两个参数,经过apply方法处理而返回一个Object对象
/** * A function that maps an array of interfaces to an optimal key where * Class objects representing interfaces are weakly referenced. */ private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> { @Override public Object apply(ClassLoader classLoader, Class<?>[] interfaces) { switch (interfaces.length) { case 1: return new Key1(interfaces[0]); // the most frequent case 2: return new Key2(interfaces[0], interfaces[1]); case 0: return key0; default: return new KeyX(interfaces); } } }
这里主要根据接口数量来分别返回不同的Key对象。
ProxyClassFactory类实现BiFunction接口,其实现传入ClassLoader和Class<?>[],返回Class<?>
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], 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(); long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;
产生代理类class数据
/* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
核心由ProxyGenerator.generateProxyClass实现
/** * Generate a proxy class given a name and a list of proxy interfaces. * * @param name the class name of the proxy class * @param interfaces proxy interfaces * @param accessFlags access flags of the proxy class */ public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags)
ProxyGenerator相当于一个小型的编译器,负责生成代理类的class文件
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile();
class文件生成具体过程
1.收集代理类所有需要代理的方法
处理hashcode equals toString方法
addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class);
处理所有的代理接口方法
for (Class<?> intf : interfaces) { for (Method m : intf.getMethods()) { addProxyMethod(m, intf); } }
2.收集代理类所有FieldInfo和MethodInfo信息
构造函数 代理方法
/* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. */ try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // add static field for method's Method object fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); // generate code for proxy method and add it methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer());
核心构造函数的产生,因为代理增强逻辑是通过InvocationHandler通过构造函数来加入到代理类class中的。
这其中涉及到反射以及class文件格式,以后慢慢研究。
/** * Generate the constructor method for the proxy class. */ private MethodInfo generateConstructor() throws IOException { MethodInfo minfo = new MethodInfo( "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", ACC_PUBLIC); DataOutputStream out = new DataOutputStream(minfo.code); code_aload(0, out); code_aload(1, out); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( superclassName, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V")); out.writeByte(opc_return); minfo.maxStack = 10; minfo.maxLocals = 2; minfo.declaredExceptions = new short[0]; return minfo; }
3.产生最终class文件
/* ============================================================ * Step 3: Write the final class file. */ /* * Make sure that constant pool indexes are reserved for the * following items before starting to write the final class file. */ cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (Class<?> intf: interfaces) { cp.getClass(dotToSlash(intf.getName())); } /* * Disallow new constant pool additions beyond this point, since * we are about to write the final constant pool table. */ cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { /* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */ // u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(accessFlags); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count; dout.writeShort(interfaces.length); // u2 interfaces[interfaces_count]; for (Class<?> intf : interfaces) { dout.writeShort(cp.getClass( dotToSlash(intf.getName()))); } // u2 fields_count; dout.writeShort(fields.size()); // field_info fields[fields_count]; for (FieldInfo f : fields) { f.write(dout); } // u2 methods_count; dout.writeShort(methods.size()); // method_info methods[methods_count]; for (MethodInfo m : methods) { m.write(dout); } // u2 attributes_count; dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) { throw new InternalError("unexpected I/O Exception", e); } return bout.toByteArray();
因此便产生最终代理类的class文件数据。
目标类通过编译成class文件而执行,用户实现InvocationHandler接口在此实现对于原始方法的增强逻辑,代理类生成class文件,通过构造函数方式将用户自定义InvocationHandler加入到代理类class文件中,从而实现代理类的增强。
查看一下生成的代理类class文件
public static void main(String[] args) throws IOException { UserService us = new UserServiceImpl(); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), (proxy1, method, args1) -> { if(method.getName().equals("hello")){ System.out.println("before"); method.invoke(us, args1); System.out.println("after"); } return 200; }); //us.hello("lisi"); proxy.hello("lisi"); byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", UserServiceImpl.class.getInterfaces()); FileOutputStream fo = new FileOutputStream("$Proxy0.class"); fo.write(bytes); fo.close(); }
得到class文件反编译如下所示:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.java.proxy.UserService; 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 UserService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(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 hello(String var1) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } 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("com.java.proxy.UserService").getMethod("hello", Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
代理类由构造函数传入InvocationHandler ,当调用代理类hello方法时,其由InvocaitonHandler来进行转发
public final int hello(String var1) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
其中m3方法由反射方法获取目标类的hello方法。
m3 = Class.forName("com.java.proxy.UserService").getMethod("hello", Class.forName("java.lang.String"));
参考文献:
https://www.jianshu.com/p/3616c70cb37b
https://blog.csdn.net/zhangdefeng2008/article/details/79399898