本文共 12621 字,大约阅读时间需要 42 分钟。
代理模式其本质是,对于需要调用的方法,在其原由逻辑上进行修改,或者说是在原有方法的基础上前后都加上log日志,或者是对原有方法的结果进行二次处理等等。
其结构图如下
目标接口,定义RealSubject和Proxy的共同接口,
具体的实现接口类,实现目标接口的功能
调用具体的实现接口类,并在具体实现类的基础上,调用其他相关方法。
静态代理的实现,我们在原来方法上,添加了前后各打印一条数据
public interface Subject { void request();}
具体方法调用
public class RealSubject implements Subject { @Override public void request() { System.out.println("具体方法实现"); }}
代理类方法实现
public class StaticProxy implements Subject { private Subject subject; public StaticProxy(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("-------start-------------"); subject.request(); System.out.println("-------end----------------"); }}
测试代码
public class Test { public static void main(String[] args) { Subject subject = new RealSubject(); StaticProxy proxy = new StaticProxy(subject); proxy.request(); }}执行结果-------start-------------具体方法实现-------end----------------
以上就是静态代理的具体实现,接下来来看jdk提供的的动态代理实现方法
接口不发生变化
public interface Subject { void request();}
具体接口的实现也不变
public class RealSubject implements Subject { @Override public void request() { System.out.println("hello!"); }}
主要是代理类,这个时候继承的是jdk给我们提供的InvocationHandler
public class DynamicProxy implements InvocationHandler { private Object target; public Object bind(Object object){ this.target = object; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke"); method.invoke(target,args); System.out.println("after invoke"); return null; } }
接下来是测试类
public class TestProxy { public static void main(String[] args) { DynamicProxy proxy = new DynamicProxy(); Subject service =(Subject) proxy.bind(new RealSubject()); service.request(); }}执行结果before invokehello!after invoke
对于动态代理我们可以在调用的时候,传递进去任何一个接口的显现类,都会在具体实现类的基础上,增加上我们代理类的逻辑
关于动态代理的实现,其主要是涉及到的就是Proxy类和InvocationHandler这个接口,我们分别来看下,对于InvocationHandlerl接口
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
这个接口只提供了一个抽象发法用来实现,子类在实现的时候,需要将具体实现类对象传递进去,然后再用反射调用具体实现类的方法,同时可以增加其他的业务逻辑。
Proxy类当中提供的方法,我们这里是用到newProxyInstance来创建对象的,我们来看下源码实现,基于JDK1.8
public static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException { // handler的非空判断 Objects.requireNonNull(h); //将代理类的接口复制出来一份 final Class [] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 根据classLoader和接口数组生成Class对象 */ Class cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 这个地方是获取到动态生成的代理类的构造方法 final Constructor cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; 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、获取到动态创建的代理的构造方法,这个构造方法的参数就是定好的,传递进去的InvocationHandler,这个是动态代理约束好的,生成的字节码中,构造方法参数就是InvocationHandler
3、判断构造方法的访问修饰符,如果不是public的,将其设置成可以访问的
4、根据传递进来的具体的Handler对象和我们上面的构造方法对象,生成一个动态代理类的对象。
上面就是整个动态代理对象的生成过程,接下来我们来看getProxyClass0方法实现
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); }
注释写的很清楚,如果当前的这个接口数组已经存在,就返回缓存当中的副本,否则的话,就会通过ProxyClassFactory去创建一个Class对象。我们来看下ProxyClassFactory是如何创建一个Class对象的吧
其实就是WeakCache这个对象当中会在get取不到值时,去生成一个值放入进去,这里不做详细分析
WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory是Proxy的一个静态内部类,主要就是用来根据classLoader和接口数组来生成Class对象的。下面我就贴出其主要用来创建Class对象的代码
@Override public Class apply(ClassLoader loader, Class [] interfaces) { Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class intf : interfaces) { Class interfaceClass = null; try { //根据接口的全限定名获取到接口的Class对象 interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } // 如果两次的Class对象不一致,直接抛出异常,说明当前classloader // 加载出来接口的Class对象和接口在项目当中的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; //下面这个循环主要是判断非pulic的接口是不是在同一个包内 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"); } } } //如果是公共的接口,我们就设置其全限定名为 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; }
这个类当中的生成代码的核心部分就是通过构建好的ProxyGenerator对象,调用其 generateClassFile()方法
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); } } }
这部分就是根据父接口生成字节码,我们调用ProxyGenerator.generateProxyClass方法,然后根据我们的RealSubject对象生成一个代理类到本地。
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces()); String path = "C:\\Users\\Administrator\\Desktop\\com\\RealSubject.class"; try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理类class文件写入成功"); } catch (Exception e) { System.out.println("写文件错误"); }
打开生成好的RealSubject.class文件
可以看到动态生成了一个$Proxy0的代理类,继承自Proxy类,实现了我们的Subject接口,其构造方法是传递了一个InvocationHandler参数
同时实现了equals,hashCode,toString三个方法,以及我们自己定义的接口当中的方法,我们主要看接口当中定义的request方法如何实现
这里我们可以清楚地看到,调用的InvocationHandler实现类当中的invoke方法,m3就是我们自定类当中定义的方法
到这里为止,我们就可以清楚地看到为什么在调用request方法的时候,会调用DynamicProxy当中的我们自己实现的invoke方法,因为传递过去的就是我们自己的InvokationHandler对象。动态代理类的构造方法生成之后,传递进去的就是我们调用
Proxy.newProxyInstance()这个方法传递的自己的实现类