1.代理模式
Proxy里面有个RealSubject对象,初始化的时候将RealSubject对象传入,进行Proxy中RealSubject的对象进行初始化,然后Proxy中的方法,都是调用Proxy对象的方法,可在前后加需求,客户端只需使用多态,构造一个接口,即可。调用接口的方法,实际上就是调用Proxy方法,即RealSubject方法。
2.静态代理
按上述类图进行操作就是静态代理,静态代理如果要对多个方法进行处理,就得在多个方法前后进行修改,不方便,因此产生动态代理。
3.动态代理
代理类在程序运行时创建的代理方式,可以方便的对代理类的函数进行统一的处理,而不用修改代理类的每个方法。
3.1整体的用法
接口
具体对象
代理类
测试类
3.2底层原理
主要关于InvocationHandler和Proxy,前面看不懂没事,坚持看完你就懂了,我也是这么过来的
InvocationHandler接口
子类重写的时候,需要将具体实现类对象(不是代理类)传递进去,然后用反射调用具体实现类的方法,同时增加其他业务逻辑。
Proxy类
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h);handler的非空判断
final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); }
Class> cl = getProxyClass0(loader, intfs);根据classloader和接口数组生成代理类的class对象
try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); }
获取动态生成的代理类中参数为InvocationHandler的构造方法 //private static final Class>[] constructorParams = { InvocationHandler.class };
final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; 得到cl代理类class中类的修饰符,然后判断是不是public类型,如果是,就返回指定构造器cons的实例,即代理对象的实例,如果不是,就将cons构造方法设置为可访问。 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } } |
1、根据传递进来的ClassLoader,以及我们的代理对象的父接口数组,来动态创建二进制的class文件,然后根据创建好的Class二进制文件,获取到创建的动态代理类的Class对象。
2、通过代理类的class对象,获取class对象中参数为InvocationHandler的构造方法
3、判断构造方法的访问修饰符,如果不是public的,将其设置成可以访问的
4、调用构造器的newInstance方法,参数为InvocationHandler,返回代理类的实例
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } proxyClassCache是WeakCache类型,如果缓存中包含接口数组和classloader的class类已经存在,就返回缓存副本,否则通过ProxyClassFactory创建一个对应的class对象 return proxyClassCache.get(loader, interfaces); } |
WeakCache这个对象当中会在get取不到值时,去生成一个值,放入进去;
ProxyClassFactory是Proxy的一个静态内部类,主要就是用来根据classLoader和接口数组来生成Class对象的。
subKeyFactory.apply(key, parameter) 方法调用的是ProxyClassFactory@apply(ClassLoader loader, Class>[] interfaces) 方法
public Class> apply(ClassLoader loader, Class>[] interfaces) { Map for (Class> intf : interfaces) {
Class> interfaceClass = null; try { 根据接口全限定类名和classLoader,获取到接口class对象 interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } 如果两次接口class对象不一致,直接抛出异常,说明创建错误 if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } 判断创建出来的接口是不是接口类型,不是就抛异常 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } 判断接口的set集合是否已经存在了该接口类,存在抛出异常,不存在就添加进去 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; 判断非public接口是不是在同一个包内,如果不是,抛出异常; 声明代理类所在的包位置, 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"); } } }
如果都是public接口设定全限定类名com.sun.proxy.$proxy0 if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; }
long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;
根据代理类全限定类名,接口数组,访问修饰符,生成代理类的字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { 根据生成的字节码,创建class对象并返回。Native方法 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } } |
|
可以看到这段代码就是设置好需要生成的类的类名,然后调用ProxyGenerator.generateProxyClass来生成代理类的字节码
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); final byte[] var4 = var3.generateClassFile(); //中间省略掉一部分代码 return var4; }
private byte[] generateClassFile() {
//将object类当中的 hashcode,equals,toString方法添加到动态代理类当中 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length;
int var3; Class var4;
//遍历父接口数据 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3];
//获取每个接口当中的方法 Method[] var5 = var4.getMethods(); int var6 = var5.length;
//遍历接口当中的方法,将接口当中的方法都添加至动态代理类当中 for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } }
Iterator var11 = this.proxyMethods.values().iterator();
//检查代理类当中的返回类型 List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); }
Iterator var15; try { // 将构造方法添加至代理类当中的方法集合中 this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator();
//遍历代理类当中的方法,此处使用两层循环,是因为方法签名相同的,可能有多个方法 while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator();
while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var16.generateMethod()); } } // 将静态代码块添加进去 this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); }
if(this.methods.size() > '\uffff') { throw new IllegalArgumentException("method limit exceeded"); } else if(this.fields.size() > '\uffff') { throw new IllegalArgumentException("field limit exceeded"); } else { /** * 省略部分代码 **/ return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } } |
生成了代理类对象,继承自Proxy,实现了subject接口,其构造方法是传递了InvocationHandler参数,同时实现了equals ,hashcode,toString方法,以及我们自己定义的request方法。
这里就能清楚的看到,代理类中,request方法,其实就是调用InvocationHandler实现类中invoke方法,m3就是RealSubject中定义的request方法
这里就能知道,调用Subject的request的时候,为什么能够调用动态代理类的invoke方法了。因为在调用bind中的Proxy.newProxyInstance的时候,传入的是我们classLoader,代理类的父接口,和自定义的InvocationHandler,因此生成的代理类对象中的h就是自定义的InvocationHandler。
整体流程:
使用方法:
定义一个接口Subject,里面有抽象方法request,一个接口的实现类RealSubject,里面有真正的request方法,定义个ProxyHandler,实现InvocationHandler接口,有ProxyHandler中有个目标对象target,用于传入RealSubject的实例;里面有两个方法,bind方法,将传入的RealSubject对象,赋值给target,并调用Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)返回代理类对象的实例;还有一个方法,invoke(Object proxy, Method method, Object[] args)方法,在method.invoke前后可以加逻辑功能;method方法对象包含方法的相关信息,method.invoke就会去找target对象中,指定参数与该方法匹配的方法。
底层原理:
首先分析代理对象的产生过程,最后分析代理对象的产生后的样子
产生过程:
Proxy.newInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this) 方法,该方法主要做了
①获取代理类的接口Subject.class对象的副本intfs。
②调用getProxyClass0(loader, intfs)方法,获取代理类的class对象
--2.1 getProxyClass方法会去调用proxyClassCache.get(loader,interfaces)方法,该方法的作用,如果缓存中已经有该接口对应的代理类的副本,那么返回代理类的副本,否则使用ProxyClassFactory的apply方法创建一个class对象;get方法中有一句表现了这个过程,如下图
SubkeyFactory与ProxyClassFactory都是BiFunction接口的的实现类,此处调用的是ProxyClassFactory的apply方法。
----2.1.1 apply方法:根据接口的全限定类名以及指定的classLoader,调用class.forName方法得到接口的class对象interfaceClass,进行判断是否是接口类型,是否有重复,
----2.1.2 设定代理类所在的包路径,如果有非public接口,且不是在同一个包内,抛异常,如果在同一个包内,就设定代理类的包路径为proxyPkg,如果都是public接口,那么生成的代理类的包路径proxyPkg设定为com.sum.proxy.
----2.1.3 根据包路径,类名前缀和num数字,确定代理类的名字
----2.1.4 调用ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);内部生成一个ProxyGenerator对象,调用ProxyGenerator.generateClassFile()得到代理类的字节码文件;
------2.1.4.1 ProxyGenerator.generateClassFile()方法,为代理类添加了三个object类的hashcode,equals,toString方法,遍历父接口,获取每个接口中的方法,添加方法,添加构造方法等,都加入到代理中;返回二进制字节码文件
----2.1.5 调用defineClass0(loader, proxyName, proxyClassFile, 0,proxyClassFile.length);将字节码文件创建为class对象,返回
③获取到代理类的class对象后,得到代理类参数为InvocationHandler的构造方法,如果是非public类型,就使用setAccessible设定为true,然后调用cons.newInstance(new Object[]{h})来生成代理类对象;
④在代理类对象中,有个request方法,方法中默认会调用Proxy类里面的InvocationHandler也就是我们自定义的InvocationHandler的invoke方法。这就实现了动态代理