内容简介:代理模式,作为常用的设计模式之一,在实际项目中或多或少都会被使用到。当前一些主流的项目框架中,也有不少代理模式的身影。代理模式中,代理类与主体类实现同样的接口,代理类持有实体类的引用,并接受客户端对代理类中实体引用的外部注入,并代理实体类的功能。注:描述中的这种外部注入形式有个专有技术名词:依赖注入
代理模式,作为常用的 设计模式 之一,在实际项目中或多或少都会被使用到。当前一些主流的项目框架中,也有不少代理模式的身影。
代理模式中,代理类与主体类实现同样的接口,代理类持有实体类的引用,并接受客户端对代理类中实体引用的外部注入,并代理实体类的功能。
注:描述中的这种外部注入形式有个专有技术名词:依赖注入
代理模式通用的类图为:
按照代理类的产生方式,是在运行期之前就静态的存在还是在运行期动态产生,可以将代理模式分为静态代理和动态代理。
二、静态代理
在真正理解动态代理之前,有必要先简单回顾下静态代理的一般过程。
直接看一个具体的实例。
1,定义接口:
package com.corn.proxy.pstatic; public interface Subject { String action(); } 复制代码
2,定义主体类:
package com.corn.proxy.pstatic; public class RealSubject implements Subject { @Override public String action() { System.out.println("action in RealSubject"); return "action done"; } } 复制代码
3,定义静态代理类:
package com.corn.proxy.pstatic; public class ProxySubject implements Subject{ private Subject realSubject; public ProxySubject(Subject realSubject) { this.realSubject = realSubject; } @Override public String action() { // 主体action前执行 System.out.println("do sth before RealSubject action"); String result = this.realSubject.action(); // 主体action后执行 System.out.println("do sth after RealSubject action"); return result; } } 复制代码
4,客户端注入实体并访问:
package com.corn.proxy.pstatic; public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); ProxySubject proxySubject = new ProxySubject(realSubject); proxySubject.action(); } } 复制代码
run,输出结果:
do sth before RealSubject action action in RealSubject do sth after RealSubject action 复制代码
上述代码过程亦对应了代理模式类图结构。
三、动态代理
本质上,动态代理也是遵循上述通用的代理模式类图关系,与静态代理相比,其 动态
主要体现在: 1,具体代理类(ProxySubject)的生成是在运行期动态产生的,而非编译期就已经静态存在;
2,具体代理类(ProxySubject)与被代理类的代理关系(ProxySubject持有RealSubject的引用),是想办法动态注入进入的;
3,具体代理类(ProxySubject)对被代理类的功能的代理是在动态生成的代理类内部,想办法去动态的调用被代理类的对应方法的。
无论是具体代理类的动态产生,还是与被代理类的关系建立,以及对被代理类方法的代理调用,这中间,都用到了两个关键的中间媒介,即Proxy和InvocationHandler。
Proxy类,其中提供了动态生成代理类的静态方法,并持有实现了InvocationHandler接口的引用。同时,所有生成的代理类也都是Proxy类的子类。
InvocationHandler接口,只包含一个抽象出来的方法名:invoke,使得实现InvocationHandler接口的类去具体实现,在实现中通过持有被代理类实体(RealSubject),并通过反射,去调用对应的实体方法。
因此,动态代理总体上的执行流程为:
当客户端通过Proxy的静态方法生成动态代理类后,调用动态代理类对应的接口方法时,内部会调用其内部持有的InvocationHandler接口的实例对象的invoke方法,并得以调用到实际被代理实体的相应方法。
将总体的类之间关系如果用类图表示,与通用的代理模式类图稍有区别。
相应代码实现代码过程如下:
1,定义接口Subject:
package com.corn.proxy.pdynamic; public interface Subject { String action(); } 复制代码
2,定义主体类:
package com.corn.proxy.pdynamic; public class RealSubject implements Subject{ @Override public String action() { System.out.println("action in RealSubject"); return "action done"; } } 复制代码
3,定义实现了InvocationHandler接口的类:
package com.corn.proxy.pdynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ProxyInvocationHandler implements InvocationHandler { protected Subject subject; public ProxyInvocationHandler(Subject subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("do something before in ProxyInvocationHandler"); return method.invoke(subject, args); } } 复制代码
4,客户端注入实体并访问:
package com.corn.proxy.pdynamic; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject); Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), proxyInvocationHandler); proxyRealSubject.action(); } } 复制代码
run,输出结果:
do something before in ProxyInvocationHandler action in RealSubject 复制代码
四、实现原理
从上述动态代理执行流程和类图分析中已经可以看出,动态代理的关键,在于通过InvocationHandler和Proxy媒介,在运行时动态生成动态代理类,生成的动态代理类依然实现了Subject接口,并在调用方法时回调了InvocationHandler实现类的invoke方法,InvocationHandler实现类的invoke方法通过反射,回调了被代理实体的对应方法。
看起来有点绕。
在生成动态代理过程中,Java工程和Android项目中有点区别,先从源码角度看下 Java 工程的具体实现过程。
Android studio中不能直接新建Java工程,但可以在Android项目中通过建立Java Library的方式,建立Java库。默认情况下,Android Studio关联的JDK为AS内置的,是看不到源码的,为此,需要将其改成自己下载的JDK路径,此路中包含了源码src目录。 具体修改方式为:
1,查看本地JDK目录
/usr/libexec/java_home -V 复制代码
输出
Matching Java Virtual Machines (1): 1.8.0_162, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home 复制代码
2,Android Studio替换JDK设置 File >> Other Setting >> Default Project Structure,在JDK Location中将默认设置
/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home 复制代码
修改为:
/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home 复制代码
完成设置后,可以直接看到JDK源码。
ProxyInvocationHandler接口只有一个方法:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } 复制代码
下面重点看下Proxy类newProxyInstance过程。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy 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<Void>() { 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); } } 复制代码
其中调用的getProxyClass0对应的具体实现为:
/** * 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); } 复制代码
于是,我们跟到对应的ProxyClassFactory:
/** * 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<?>> { // 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()); } } } 复制代码
其中,关键的过程在于:
/* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { ... } 复制代码
通过ProxyGenerator类中的generateProxyClass方法生成对应的动态代理类二进制代码,并通过 ClassLoader加载后,通过反射,生成对应的类实例。
ProxyGenerator类是直接集成在rt.jar中的,包名为sun.misc,为扩展类。Android项目中因JDK版本问题,默认是不集成的,那Android中此处是如何实现的呢?
同样的,在Android项目中直接使用动态代理,跟踪源码调用过程。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); // Android-removed: SecurityManager calls /* final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } */ /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { // Android-removed: SecurityManager / permission checks. /* if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } */ final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { // BEGIN Android-changed: Excluded AccessController.doPrivileged call. /* AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); */ cons.setAccessible(true); // END Android-removed: Excluded AccessController.doPrivileged call. } 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); } } 复制代码
很明显,Android JDK中newProxyInstance方法中部分实现做了修改。但主要执行路径依然不变。
/** * 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); } 复制代码
同样的,ProxyClassFactory类。
/** * 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<?>> { // 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 the default package. proxyPkg = ""; } { // Android-changed: Generate the proxy directly instead of calling // through to ProxyGenerator. List<Method> methods = getMethods(interfaces); Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); validateReturnTypes(methods); List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods); Method[] methodsArray = methods.toArray(new Method[methods.size()]); Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]); /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; return generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); } } } 复制代码
其中关键的,我们发现
// Android-changed: Generate the proxy directly instead of calling // through to ProxyGenerator. ... return generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); 复制代码
跟踪进去看下具体的执行过程
@FastNative private static native Class<?> generateProxy(String name, Class<?>[] interfaces, ClassLoader loader, Method[] methods, Class<?>[][] exceptions); // END Android-changed: How proxies are generated. 复制代码
原来,Android JDK中对generateProxy进行了处理,直接使用的是本地的方法。
那么,生成动态代理时,如果遇到线程安全问题呢?
ProxyClassFactory代码中,有如下处理过程:
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(); ... /* * 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) { ... } } 复制代码
显然,通过线程安全的文件命名方式,预防了针对同样的接口使用多线程情况下,使用动态代理可能出现的线程安全问题。
那么,生成的动态代理到底长什么样子?我们可以直接调用系统的ProxyGenerator.generateProxyClass试一下。
package com.corn.proxy.pdynamic; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject); Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), proxyInvocationHandler); proxyRealSubject.action(); String proxyName = "ProxySubject"; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, RealSubject.class.getInterfaces()); try (FileOutputStream fos = new FileOutputStream("/Users/corn/T/ProxySubject.class")){ fos.write(proxyClassFile); fos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 复制代码
run,对应目录下生成ProxySubject.class文件,通过 工具 可以看到对应的字节码内容。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.corn.proxy.pdynamic.Subject; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class ProxySubject extends Proxy implements Subject { private static Method m1; private static Method m2; private static Method m0; private static Method m3; public ProxySubject(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 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); } } public final String action() throws { try { return (String)super.h.invoke(this, m3, (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"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m3 = Class.forName("com.corn.proxy.pdynamic.Subject").getMethod("action"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } 复制代码
果然,在生成的动态代理中,实现了Subject接口,并在对应的action方法中,调用了InvocationHandler实现类的实例的invoke方法。
五、结语
动态代理作为代理模式中的一种,避免了在运行期之前直接定义静态代理类,对于需要大量的代理类的情形下,是非常有用的。同时,我们也应该看到,在整体流程上,其实动态代理与静态代理总体上还是一样的。动态代理无论在代理类的创建过程中,还是对代理方法的调用,过程中都用到了反射,在一定程度上性能上有所损耗,实际使用中需要适量权衡。
动态代理模式,作为设计模式中相对比较难理解的一种,主要在于其过程经过了层层封装,最后只是通过Proxy类和InvocationHandler对外直接暴露了使用接口,对使用方直接屏蔽了具体的细节。但对于我们理解这种模式本身来说,了解并适当掌握其中的过程,还是有所受益的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 你必须会的 JDK 动态代理和 CGLIB 动态代理
- 彻底搞懂jdk动态代理并自己动手写一个动态代理
- Java 静态代理和动态代理的使用及原理解析
- 动态代理与RPC
- JAVA动态代理
- 动态代理+注解(DynamicProxyAndAnnotations)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Docker开发指南
[英] Adrian Mouat / 黄彦邦 / 人民邮电出版社 / 2017-4 / 79.00元
Docker容器轻量和可移植的特性尤其适用于动态和分布式的环境,它的兴起给软件开发流程带来了一场革命。本书对Docker进行了全面讲解,包括开发、生产以至维护的整个软件生命周期,并对其中可能出现的一些问题进行了探讨,如软件版本差异、开发环境与生产环境的差异、系统安全问题,等等。一起来看看 《Docker开发指南》 这本书的介绍吧!