Java中的动态代理详解
前言
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能。
学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,它利用的是反射机制,依赖注入就不用多说了;而对于Spring的核心AOP来说,使用了动态代理,其实底层也是反射。我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制,所以本篇随笔就是对java的动态机制进行一个回顾。
目录
1、代理设计模式
2、Proxy 类和 InvocationHandler 接口
3、代理机制创建过程及其特点
3.1、动态代理实例创建过程
3.2、动态代理类特点
3.2.1、生成动态代理实例的包名
3.2.2、类修饰符
3.2.3、类名
3.2.4、类继承关系
3.3、动态代理类实例特点
3.3.1、关联调度器
3.3.2、代理接口
3.3.3、异常处理
4、Proxy类源码解析
4.1、几个重要的成员变量
4.2、newProxyInstance 方法
4.2.1、checkProxyAccess 方法
4.3、getProxyClass0方法(动态代理类的核心)
4.3.1、proxyClassCache 缓存表调用 get 方法
4.3.2、KeyFactory.apply方法
4.3.3、supplier.get()方法
4.3.4、ProxyClassFactory.apply方法(核心)
4.3.5、ProxyGenerator.generateProxyClass
4.4、WeekCache 缓存表
5、代码演练
6、美中不足
1、代理设计模式
代理是一种常用的设计模式,其目的就是为某个对象提供一个代理以控制对该对象的访问。代理类负责为委托类 预处理消息、过滤消息、转发消息,以及进行消息被委托类执行后的后续处理。
图 1. 代理模式
为了保持行为的一致性,代理类ProxySubject和委托类RealSubject会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
2、Proxy 类和 InvocationHandler 接口
要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
Proxy 的静态方法:
// 方法 1: 该方法用于获取指定代理对象 所关联的调用处理器,参数:代理对象(Object proxy)
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取动态代理类的类对象,参数:1、委托对象的类装载器,2、委托对象的一组接口。
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类,参数:类对象
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于生成动态代理类实例,参数:1、委托对象的类装载器,2、委托对象的一组接口,3、调用处理器
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
java.lang.reflect.InvocationHandler:
这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
Object invoke(Object proxy, Method method, Object[] args)
// 该方法负责集中处理动态代理类上的所有方法调用。参数:1、动态代理类对象,2、被调用的方法对象,3、调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行。
3、代理机制创建过程及其特点
3.1、动态代理实例创建过程
首先让我们来了解一下如何使用 Java 动态代理。具体有如下四步骤:
1、通过实现 InvocationHandler 接口创建自己的调用处理器:
InvocationHandler invocationHandler = new InvocationHandlerImpl(realSubject); //realSubject 委托对象(继承并实现了接口)
2、通过为 Proxy 类指定委托对象的 ClassLoader 和 interface数组 来创建动态代理类:
Class<?> proxyClass = Proxy.getProxyClass(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces());
3、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型:
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入:
Object proxy = constructor.newInstance(invocationHandler);
实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程,Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法,
其作用就是得到一个动态的代理对象,其接收三个参数,具体如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载,实际使用过程中传入委托对象 的加载器。
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,
那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了,实际使用过程传入的是委托对象的 接口数组。
h:一个 InvocationHandler 对象,当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上。
3.2、动态代理类特点
3.2.1、生成动态代理实例的包名
- 如果所代理的接口都是 public 的,那么它将被定义在顶层,新生成的代理类所在的包为:com.sun.proxy.$Proxy0。
- 如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 dynamic.proxy.sample包中的某非 public 接口 A,那么新生成的代理类所在的包就是 dynamic.proxy.sample.$Proxy0),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
3.2.2、类修饰符
该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
3.2.3、类名
格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类, 值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加, 原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象, 而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
3.2.4、类继承关系
该类的继承关系如图:接下来让我们来了解一下 Java 动态代理机制的一些特点。
图 2. 动态代理类的继承图
由图可见,Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。
3.3、动态代理类实例特点
3.3.1、关联调度器
每个代理类实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler 去获得代理类实例的调用处理器对象。在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行,此外,值得注意的是,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,可能的原因有:一是因为这些方法为 public 且非 final 类型,能够被代理类覆盖;二是因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。当代理的一组接口有重复声明的方法且该方法被调用时,代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,因为在代理类内部无法区分其当前的被引用类型。
3.3.2、代理接口
首先,要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。其次,这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败。再次,需被代理的所有非 public 的接口必须在同一个包中,否则代理类生成也会失败。最后,接口的数目不能超过 65535,这是 JVM 设定的限制。
3.3.3、异常处理
从调用处理器接口 invoke 中可以看到理论上它能够抛出任何类型的异常,因为所有的异常都继承于 Throwable 接口,但事实是否如此呢?答案是否定的,原因是我们必须遵守一个继承原则:即子类覆盖父类或实现父接口的方法时,抛出的异常必须在原方法支持的异常列表之内。所以虽然调用处理器理论上讲能够,但实际上往往受限制,除非父接口中的方法支持抛 Throwable 异常。那么如果在 invoke 方法中的确产生了接口方法声明中不支持的异常,那将如何呢?放心,Java 动态代理类已经为我们设计好了解决方法:它将会抛出 UndeclaredThrowableException 异常。这个异常是一个 RuntimeException 类型,所以不会引起编译错误。通过该异常的 getCause 方法,还可以获得原来那个不受支持的异常对象,以便于错误诊断。演示如下:
①、public class RealSubject implements Subject1,Subject2{..}委托对象接口异常
②、动态代理实例实际调用
③、调用处理 invoke 反射调用
④、异常堆栈快照
4、Proxy类源码解析
4.1、几个重要的成员变量
/*获取Proxy类构造器的参数*/
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
/*Proxy类缓存表*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/*生成动态代理类(Proxy)实例的相关联的调用处理器 */
protected InvocationHandler h;
4.2、newProxyInstance 方法
@CallerSensitivepublic 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);}//查找或者生成代理类Class<?> cl = getProxyClass0(loader, intfs);// 生成一个动态代理类的构造函数,参数为:constructorParamstry {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);}}
4.2.1、checkProxyAccess 方法
private static void checkProxyAccess(Class<?> caller,ClassLoader loader,Class<?>... interfaces){SecurityManager sm = System.getSecurityManager();if (sm != null) {ClassLoader ccl = caller.getClassLoader();//加载器校验if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);} //接口数组包权限校验ReflectUtil.checkProxyPackageAccess(ccl, interfaces);}}
4.3、getProxyClass0方法(动态代理类的核心)
当验证完权限之后,查看如何获取代理类的getProxyClass0方法,具体如下:
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {//接口数量检查if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}//从缓存中获取,如果不存在就创建return proxyClassCache.get(loader, interfaces);/* proxyClassCache 成员定义类型private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
*/
}
4.3.1、proxyClassCache 缓存表调用 get 方法
使用 proxyClassCache 做缓存,其目的是为了复用,同时防止多线程重复创建。在 weekCache 类中使用了多个 map 进行记录,稍后我们再做详细介绍。先来看一下该类的 get 方法
public V get(K key, P parameter) { //key 为加载器,para 为接口数组Objects.requireNonNull(parameter);//删除过期条目expungeStaleEntries();//创建cacheKeyObject cacheKey = CacheKey.valueOf(key, refQueue);查看 cacheKey 是否已经存在 valuemaps 中ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);/* map成员变量类型private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map= new ConcurrentHashMap<>();
*/if (valuesMap == null) {// valuesMap 不存在,则新建一个放入map中ConcurrentMap<Object, Supplier<V>> oldValuesMap= map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());if (oldValuesMap != null) {valuesMap = oldValuesMap;}}//生成代理类的 subKey 为弱引用类型Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));//尝试从 valuemap 中获取代理类的供给器 supplier Supplier<V> supplier = valuesMap.get(subKey);Factory factory = null;while (true) {//如果确实已经存在 supplier 直接获取代理类if (supplier != null) {// supplier might be a Factory or a CacheValue<V> instanceV value = supplier.get();if (value != null) {return value;}}// else no supplier in cache// or a supplier that returned null (could be a cleared CacheValue// or a Factory that wasn't successful in installing the CacheValue)// lazily construct a Factory// 不存在创建一个supplier,注意factory实现了supplierif (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);}if (supplier == null) {//如果不存在则保存到 valuemap 中supplier = valuesMap.putIfAbsent(subKey, factory);if (supplier == null) {// supplier 添加成功supplier = factory;}// else retry with winning supplier} else {// 创建的时候发现已经有了,尝试替换if (valuesMap.replace(subKey, supplier, factory)) {// 替换成功// cleared CacheEntry / unsuccessful Factory// with our Factorysupplier = factory;} else {// retry with current suppliersupplier = valuesMap.get(subKey);}}}}
4.3.2、KeyFactory.apply方法
缓存表调用 get 方法首先去查看是否存在缓存过期的情况,存在则清除掉。如果不存在,尝试的生成 key 和 value 的相关元数据
,下面介绍 key 的生成方法 subKeyFactory.apply(key, parameter) 方法:
//根据接口个数的不同选择生成不同的key对象
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {switch (interfaces.length) {case 1: return new Key1(interfaces[0]); // the most frequentcase 2: return new Key2(interfaces[0], interfaces[1]);case 0: return key0;default: return new KeyX(interfaces);}
}
4.3.3、supplier.get()方法
根据 subKeyFactory.apply(key, parameter) 生成的 subKey 从 valueMap 中获取供给器 supplier。然后在判断是否存在同时其他线程生成,然后就是尝试着保存添加信息,如果已经有了就尝试替换。动态代理类对象 proxyClass 最终是通过supplier.get()
方法获取的,具体如下:
private final class Factory implements Supplier<V> {private final K key;private final P parameter;private final Object subKey;private final ConcurrentMap<Object, Supplier<V>> valuesMap;Factory(K key, P parameter, Object subKey,ConcurrentMap<Object, Supplier<V>> valuesMap) {this.key = key;this.parameter = parameter;this.subKey = subKey;this.valuesMap = valuesMap;}@Overridepublic synchronized V get() { // serialize access// re-checkSupplier<V> supplier = valuesMap.get(subKey);if (supplier != this) {//再次校验是否匹配//因为此方法调用之前有可能发生valuesMap.replace(subKey, supplier, factory)return null;}// else still us (supplier == this)// create new valueV value = null;try {//真正的获取 proxyClass 的逻辑,重点方法value = Objects.requireNonNull(valueFactory.apply(key, parameter));} finally {if (value == null) { // remove us on failure// 如果最终没能生成代理对象,从 valuemap 移除 suppliervaluesMap.remove(subKey, this);}}// the only path to reach here is with non-null valueassert value != null;// //包装 value 为 cacheValueCacheValue<V> cacheValue = new CacheValue<>(value);// cacheValue 放进 reverseMapreverseMap.put(cacheValue, Boolean.TRUE);// 尝试这替换valuemap中的cacheValue,这个步骤应该一直成功//笔者不太理解 this 和 cacheValue 不同类型咋替换if (!valuesMap.replace(subKey, this, cacheValue)) {throw new AssertionError("Should not reach here");}// successfully replaced us with new CacheValue -> return the value// wrapped by itreturn value;}}
4.3.4、ProxyClassFactory.apply方法(核心)
在 supplier.get() 方法中可以看到 value 即 proxyClass 的获取是调用 valueFactory.apply(key, parameter) 获取的,参数依然是 key 为委托对象加载器,para 为委托对象接口数组。ProxyClassFactory类的apply方法,具体如下:
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>{// prefix for all proxy class namesprivate static final String proxyClassNamePrefix = "$Proxy";// next number to use for generation of unique proxy class namesprivate static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic 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");}//验证是否为接口if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}//验证接口不是重复的if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}//修饰符String proxyPkg = null; // package to define proxy class inint 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)) {//如果不是public类型的接口又不在同一个包下抛出异常throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg == null) {// 没有包使用默认的包 com.sun.proxyproxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}//代理类的名称 按顺序递增 => $proxy0long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;//生成代理类的字节数组byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {//调用native方法生成Classreturn defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}}}
ProxyClassFactory.apply 方法主要的步骤如下:
1.尝试着用现有的类加载器加载接口,如果成功
2.验证是否为接口,接口是否重复 ,如果成功
3.验证接口访问权限,如果成功
4.获取包的信息,和类名设置,
5.生成代理的字节数组
6.通过native方法defineClass0获取字节数字的具体的Class
最终实际的逻辑在supplier.get()
方法中,下面看一下具体的过程
4.3.5、ProxyGenerator.generateProxyClass
//生成代理类public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);/*** 生成具体文件字节数组* 1.找到所有接口的方法* 2.添加object类的三个方法 tostring hashcode equils* 3.遍历生成具体的代理方法,代理方法的逻辑都想似,回调我们的代理类*/final byte[] var4 = var3.generateClassFile();// private static final boolean saveGeneratedFiles = GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();//这就是我们为什么设置sun.misc.ProxyGenerator.saveGeneratedFiles = true的原因,设置后就会生成代理类的文件if (saveGeneratedFiles) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {try {int var1 = var0.lastIndexOf(46);Path var2;if (var1 > 0) {//生成path 将.替换成系统文件分隔符Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));//创建文件夹Files.createDirectories(var3);//具体文件var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");} else {//没包就放在项目根目录下var2 = Paths.get(var0 + ".class");}//写入到文件中Files.write(var2, var4, new OpenOption[0]);return null;} catch (IOException var4x) {throw new InternalError("I/O exception saving generated file: " + var4x);}}});}return var4;}
ProxyGenerator.generateProxyClass 主要步骤如下:
1.找到所有接口的方法
2.添加object类的三个方法 tostring hashcode equils
3.遍历生成具体的代理方法,代理方法的逻辑都想似,回调我们的代理类
4.4、WeekCache 缓存表
从我们的代码中我们可以看到 WeekCache 中使用多个 map 进行记录,Proxy 类中的 proxyClassCache 定义为 WeekCache 类型的缓存映射表,WeekCache 中几个重要的成员如下:
//① cachekey 的引用队列
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
// ② 最外层map,key=>cacheKey,value => valueMap
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//③ 记录保存value的Supplier对象map
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//④ key生成对象
private final BiFunction<K, P, ?> subKeyFactory;
//⑤ value生成对象
private final BiFunction<K, P, V> valueFactory;
map中的 key是 cacheKey(通过key+refQueue得到),是一种弱应用类型的对象,通过 cacheKey 获取到 valuesMap。
reverseMap 的 key 是 cacheValue,同为一种弱应用类型的对象.。
两者也同时为内存回收的主要对象,当某个 map 中的 key 失效的时候,在下一次进行 get,containsValue,size三个方法的时候都会触发 expungeStaleEntries 方法,然后将 value 从 reverseMap 中清除, valuemap 从 map 中清除。而 refQueue的回收,是由Reference 中 ReferenceHandler 轮询去回收的.如果回收了,refQueue.poll 会成功触发,然后就想清除操作。
private void expungeStaleEntries() {CacheKey<K> cacheKey;while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {cacheKey.expungeFrom(map, reverseMap);}
}
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,ConcurrentMap<?, Boolean> reverseMap) {// removing just by key is always safe here because after a CacheKey// is cleared and enqueue-ed it is only equal to itself// (see equals method)...ConcurrentMap<?, ?> valuesMap = map.remove(this);// remove also from reverseMap if neededif (valuesMap != null) {for (Object cacheValue : valuesMap.values()) {//移除弱应用CacheValuereverseMap.remove(cacheValue);}}
}
valueMap 是回记录真正的代理类相关信息
key => subKeyFactory.apply(key, parameter) 通过classLoader和interface[]组成
value=> supplier=>Factory 或者 CacheValue
valuemap 中 value 的两种形式:
① 刚创建时为 factory 对象
② factory.applay方法执行后会替换为 CacheValue,并且将CacheValue保存到reverseMap中
5、代码演练
首先我们定义了一个Subject1类型的接口,为其声明了两个方法:
package dynamic_proxy;public interface Subject1{public void rent1();public String hello1(String str);
}
再定义一个Subject2类型的接口,为其声明了两个方法:
package dynamic_proxy;public interface Subject2 {public void rent2();public String hello2(String str);
}
接着,定义了一个类来实现这两个接口,这个类就是我们的真实对象,RealSubject类:
package dynamic_proxy;public class RealSubject implements Subject1,Subject2{@Overridepublic void rent1(){System.out.println("I want to rent1 my house");}@Overridepublic String hello1(String str){return("hello1," + str);}@Overridepublic void rent2(){System.out.println("I want to rent2 my house");}@Overridepublic String hello2(String str){return("hello2," + str);}}
下一步,我们就要定义一个动态代理类了,前面说个,每一个动态代理类都必须要实现 InvocationHandler 这个接口,因此我们这个动态代理类也不例外:
package dynamic_proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class DynamicProxy implements InvocationHandler
{// 这个就是我们要代理的真实对象private Object subject;// 构造方法,给我们要代理的真实对象赋初值public DynamicProxy(Object subject){this.subject = subject;}@Overridepublic Object invoke(Object object, Method method, Object[] args)throws Throwable{Object ret=null;//System.out.println("object:" + object.getClass().getName());System.out.println("method:" + method);// 在代理真实对象前我们可以添加一些自己的操作System.out.println("before invoke..........");// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用ret = method.invoke(subject, args);// 在代理真实对象后我们也可以添加一些自己的操作System.out.println("after invoke..........");return ret;}}
最后,来看看我们的Client类:
package dynamic_proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;public class Client
{public static void main(String[] args){//我们要代理的真实对象RealSubject realSubject = new RealSubject();//创建调度器。参数:我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的InvocationHandler handler = new DynamicProxy(realSubject);/** 通过Proxy的newProxyInstance方法直接创建动态代理类实例,我们来看看其三个参数* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口组,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上*///System.out.println(handler.getClass().getClassLoader().getParent());//System.out.println(realSubject.getClass().getClassLoader());// Class<?> []class_interfaces =realSubject
// .getClass().getInterfaces();
// for (int i = 0; i < class_interfaces.length; i++) {
// System.out.println(class_interfaces[i].getName());
// }Object raw_object = Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);System.out.println(raw_object.getClass().getName());Subject1 subject1 = (Subject1)raw_object;Subject2 subject2 = (Subject2)raw_object;subject1.rent1();System.out.println(subject2.hello2("world"));}
}
我们先来看看控制台的输出:
com.sun.proxy.$Proxy0
before invoke..........
I want to rent1 my house
after invoke..........
before invoke..........
after invoke..........
hello2,world
我们首先来看看 $Proxy0 这东西,我们看到,这个东西是由
System.out.println(raw_object.getClass().getName());
;这条语句打印出来的,那么为什么我们返回的这个代理对象的类名是这样的呢?
Object raw_object = Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
接着我们来看看这两句
subject1.rent1();System.out.println(subject2.hello2("world"));
这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行:
@Overridepublic Object invoke(Object object, Method method, Object[] args)throws Throwable{Object ret=null;//System.out.println("object:" + object.getClass().getName());//System.out.println("method:" + method);// 在代理真实对象前我们可以添加一些自己的操作System.out.println("before invoke..........");// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用ret = method.invoke(subject, args);// 在代理真实对象后我们也可以添加一些自己的操作System.out.println("after invoke..........");return ret;}
我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们的这个 method 对象是这样的:
method:public abstract void dynamic_proxy.Subject1.rent1()
method:public abstract java.lang.String dynamic_proxy.Subject2.hello2(java.lang.String)
这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。
6、美中不足
诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例
Java中的动态代理详解相关推荐
- JAVA动态代理详解
JAVA动态代理详解 问题 1:什么是静态代理,动态代理? 2:动态代理的好处? 什么是静态代理 以生活中例子来看,我作为某某品牌面膜的北京区代理,我替厂家卖面膜,我属于代理,厂家属于委托方. 联系到 ...
- java中list和map详解
java中list和map详解 一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedL ...
- Java中的static关键字详解
** Java中的static关键字详解 ** 在一个类中定义一个方法为static,即静态的,那就是说无需本类的对象就可以调用此方法.调用一个静态方法就是 "类名.方法名" ,静 ...
- java中的进制输出转换_Java I/O : Java中的进制详解
作者:李强强 上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算.这一讲,泥瓦匠带你走进Java中的进制详解. 一.引子 在Java世界里,99%的工作都是处理这高层. ...
- Java中的main()方法详解
源文作者:leizhimin 源文链接:http://lavasoft.blog.51cto.com/62575/53263 源文作者版权申明: 版权声明:原创作品,允许转载,转载时请务必以超链 ...
- 吃透Java中的动态代理
动态代理在Java中是很重要的一部分,在很多框架中都会用到,如Spring中的AOP.Hadoop中的RPC等.为此在这把我对Java中的动态代理的理解分享给大家,同时写了一个模拟AOP编程的实例.( ...
- java中Freemarker list指令详解
java Freemarker中list指令主要是进行迭代服务器端传递过来的List集合. 定义 <#list nameList as names> ${names} </#list ...
- Java中的Runtime类详解
Java中的Runtime类详解 1.类注释 /**Every Java application has a single instance of class Runtime that allows ...
- java中properties作用,Java中Properties的使用详解
Java中有个比较重要的类Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支 持的配置文件,配置文件中很多变量是经常改变的,这样做也 ...
最新文章
- Numpy的介绍和优势
- bom头解释方法和去掉方法
- 如何增加SAP_ALL的权限
- 内置锁的能力不足以满足需求
- android中shape的属性,android中shape的属性
- robot 用AP连PC
- (转)用 Asterisk 搭建自己的免费 VoIP 服务器
- 人脸识别数据集之MS-Celeb-1M
- 上下文无关文法(例题+计算)
- 使用opencv测量点到线的距离
- 高速公路测量计算CASIO程序全套
- Counterfit 部署教程
- pandas学习-task2
- 记住这36条创业军规
- Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004
- 关于Flume断点续传(防止重复消费)的解决方案
- 数字IC设计学习笔记(一)——逻辑综合简介
- Mongodb std::exception::what(): basic_filebuf::underflow error reading the file: iostream error
- python有趣小程序-搞几款由quot;Python”语言编写的quot;有趣、恶搞、好玩”的程序代码!...
- 关于BH1750的使用说明