在Java中,动态代理是非常重要的一个概念,是一种实现 AOP(面向切面编程)的方式,具有以下优点:
总之,Java 动态代理可以帮助开发人员实现更加灵活、可扩展和易维护的代码,提高代码的复用性和可维护性,降低系统的耦合度,使代码更加健壮和安全。
同时,该技术在多种优秀开源框架中都有使用,比如mybatis框架等。理解该技术不仅有助于我们提升代码编写质量,也有利于我们在阅读框架源码时,更好的理解其实现思想。网上有很多介绍该原理的文章,但是对于代码而言我觉得,最好的理解方式就是能够深入源码中,究其根本来理解实现原理。才能不至于今天看完感觉理解了,后续再使用的时候就忘记了。
接下来,跟随源码来一起分析一下**Proxy类的实现原理**,以及**代理类是如何创建出来的。**
我们日常在使用Proxy类创建代理类时,采用的方法往往调用下面的静态方法来生成一个代理类:
Object proxyInstance = Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler h)
**这里我们首先要思考的一个问题就是:为什么要传入classLoader、interfaces参数**
带着这个疑问,我们首先进入到这个方法的源码中:
@CallerSensitive
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.
\*/
// 1.根据传入的类加载器和接口类数组,生成相应的代理类
Class<?> cl = getProxyClass0(loader, intfs);
/\*
\* Invoke its constructor with the designated invocation handler.
\*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// constructorParams = { InvocationHandler.class };
// 2. 即获取代理类中包含InvocationHandler类的构造器
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;
}
});
}
// 3. 利用该构造器创建一个包含InvocationHandler h参数的代理类实例
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.2.3步。首先就是根据传入的类加载器和接口类数组,生成相应的代理类,然后获取到包含InvocationHandler类的构造器,最后根据该构造器生成一个代理类实例。到这里,我们只能看到大致的生产代理类的实现流程,但是更细节的部分都被封装起来了,因此我们需要更进一步进行分析,而最关键的方法就是getProxyClass0(loader, intfs);
。
还记得我们上面提出的问题吗?带着这个问题,我们进入getProxyClass0(loader, intfs);
来一探究竟,即:代理类是如何创建的?
/\*\*
\* 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);
}
这里只有一行:return proxyClassCache.get(loader, interfaces);
,但是根据其名称我们也应该能意识到,proxyClassCache
应该就是一个包含代理类的缓存,目的是为了更方便的进行获取。即,如果缓存中如果没有该代理类,则调用相应方法进行创建,否则直接获取。因此,进入get
方法中,我们来找一找是在哪里调用的方法来创建的实体类。
/\*\*
\* Look-up the value through the cache. This always evaluates the
\* {@code subKeyFactory} function and optionally evaluates
\* {@code valueFactory} function if there is no entry in the cache for given
\* pair of (key, subKey) or the entry has already been cleared.
\*
\* @param key possibly null key
\* @param parameter parameter used together with key to create sub-key and
\* value (should not be null)
\* @return the cached value (never null)
\* @throws NullPointerException if {@code parameter} passed in or
\* {@code sub-key} calculated by
\* {@code subKeyFactory} or {@code value}
\* calculated by {@code valueFactory} is null.
\*/
// key 表示缓存键,parameter 表示参数。它的返回值类型为 V,表示缓存值。
public V get(K key, P parameter) {
// 判断parameter不能为空
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
// 该方法将 key 和 refQueue 作为参数调用 CacheKey.valueOf() 方法,生成一个缓存键 cacheKey,并从缓存中获取与该缓存键相关的 valuesMap。
// 如果 valuesMap 不存在,则创建一个新的 ConcurrentHashMap 对象,并将其加入到缓存中。valuesMap 是一个 ConcurrentMap 对象,它用于存储缓存键下的子键和值的提供者,其中子键由 subKeyFactory 参数生成。
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 利用Key的工厂类根据key和parameter参数生成键
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 根据该键获取到Supplier对象,表示结果的提供者。Supplier接口包含了一个get方法
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// supplier可能是一个工厂类实例,或者是一个CacheValue<V>实例
V 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
if (factory == null) {
// 核心的创建代理类的原理在Factory类里实现
// Factory类实际上也是Supplier接口的实现类,实现了get方法
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
// 将上面的Factory类对象赋值给suppler,在下一次循环时,就可以调用Factory类的get方法来生成代理类
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
这个方法里的实现就没那么好看了,因为包含很多底层的实现类平时可能比较少见过。但是就像我们上面分析的get
方法里一定有一个地方是用来在该代理类不存在时创建代理类的方法。
实际上,上面的大部分代码都是在根据参数创建缓存键,根据缓存键判断缓存中是否存在代理类。这里真正核心的功能逻辑在于:
factory = new Factory(key, parameter, subKey, valuesMap);
:// 创建Factory对象,Factory类是Supplier接口的实现类supplier = factory;
// 当缓存中不存在代理类时,将supplier=factory对象V value = supplier.get();
// 调用Factory类的get方法来创建代理类 / 调用CacheValue类的get方法直接获取代理类**这里代理类实际上被封装到Supplier接口类中,可能是Factory类,或者是CacheValue类。 ** **因此,判断supplier对象是否为空,如果为空,则说明缓存中不存在,那么会调用Factory类来创建代理类,否则,会从CacheValue类中直接取出。**
接下来,我们进入到Factory类的get方法中,对其创建代理类的过程一探究竟
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
// 这里就是创建代理类的地方,实际上是调用了valueFactory.apply的方法来创建的
// 这里的valueFactory引用实际上指向的是一个ProxyClassFactory实例,因此调用的是ProxyClassFactory.apply方法
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
// 创建代理类后存储在CacheValue对象中
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
// 替换valuesMap中subKey对应的值,原先在外层没有代理类时,该值(supplier)对应的是factory实例(supplier=factory)
// 在创建完代理类后,替换成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 it
return value;
}
上面的代码中,核心逻辑在于:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
//这里的valueFactory引用实际上指向的是一个ProxyClassFactory实例,因此调用的是ProxyClassFactory.apply方法。也就是利用ProxyClassFactory工厂类来创建代理类。if (!valuesMap.replace(subKey, this, cacheValue)).
//调用replace方法来将原先supplier=factory替换成supplier=cacheValue。这样后续再获取就可以直接返回,而不需要再次创建。至此,我们就终于找到了创建代理类的实际实现逻辑代码,就在ProxyClassFactory
类的apply
方法中!!胜利的曙光就在眼前,因此接下来我们就再进入到apply
方法中一探究竟
@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.
\*/
// 记录非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");
}
}
}
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.
\*/
// 确定代理类名
// proxyClassNamePrefix = "$Proxy"
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/\*
\* Generate the specified proxy class.
\*/
// 根据代理类的名称,需要实现的接口,访问修饰符等信息生成代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 利用类加载器将代理类的字节码文件加载到JVM中,并将其转换成可执行的 Java 类
// defineClass0是一个native方法,是由 JVM 实现的本地代码
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());
}
}
上面这段代码就是最终的代理类生成部分的核心代码了。实际上跟Java类的加载核心流程无异,只是这里的代理类是运行时动态生成的,因此我们需要在运行时(Runtime),来动态地生成类的字节码文件,并利用类加载器进行加载到JVM中运行。
这里生成类字节码文件的逻辑在ProxyGenerator.generateProxyClass
方法中,关于该方法的内部我就不详细解析了,**实际上,里面的内容也比较好理解,它会首先在类中创建hashCode
、equals
、toString
这些标准类方法,然后遍历所有的接口,利用反射获取到接口中的所有方法,再遍历接口中的这些方法,创建在代理类中。**
部分核心代码如下所示:
this.addProxyMethod(hashCodeMethod, Object.class); // 创建hashCode方法
this.addProxyMethod(equalsMethod, Object.class); // 创建equals方法
this.addProxyMethod(toStringMethod, Object.class); // 创建toString方法
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);
}
}
...
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
...
// 在常量池中指定代理类的父类为Proxy类
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
// 最后将类文件以字节数组的形式输出
return var13.toByteArray();
最初我看完上面代码之后,一直在思考,没有发现在代理类创建时,有创建带有InvocationHandler
类的构造方法,所以不明白既然这样的话,怎么能像最初newProxyInstance
方法里那样,去创建一个带有InvocationHandler
类的构造器。后面,我再仔细观察就发现了var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
这行代码,它实际上写的是数字,我猜测应该是往常量池中写入指定了代理类的父类为Proxy
类,因而继承了Proxy
的有参构造方法。
**最终将代理类文件以字节数组的形式输出,并调用JVM本地方法,利用传入的类加载器将代理类的字节码文件加载都内存中,从而生成能够执行的代理类。**
至此,我们基本上过完了Proxy
类创建代理类的实现原理,也找到了最终生成代理类的核心逻辑和原理。
现在,我们可以来回答最初提到的那个问题了。
传入类加载器是因为生成代理类的字节码文件后,需要类加载器才能将字节码文件加载到JVM中去执行。我们日常编写的Java类不需要这样显式的指定类加载器来加载,是因为在编译时,就执行了上面的类加载过程。而现在的代理类是在Java运行时动态创建的,因此需要显式传入类加载器来加载。
而传入interfaces
参数就更好理解了,实际上就是指定我们的代理类要去代理哪些类。通过上面的源码分析我们也已经知道了,代理类在生成时,会创建指定interfaces
参数相应类的所有方法。这样再相同的方法时,实际上就是会进到代理类的对应方法中去执行,而在代理类的对应方法中执行时,会触发传入的InvocationHandler
类的invoke
方法,从而实现了对原interfaces
参数相应类的代理。
最后,我们再来进一步探讨一个问题
代理类加载器应该如何传入?或者说应该传入什么类型的加载器 是否有什么原则?
通常来说,如果被代理类是日常开发的Java类的话,传入应用程序类加载器即可。这里的原理在于:
**代理类加载器需要能够访问到被代理类中引用到的其他类,例如被代理类的方法的参数类型、返回值类型等等(不然不就加载不了了嘛)。因此实际上就是,代理类的类加载器类型应该与被代理类的类加载类型一致。**
以上,就是整个Proxy类实现代理类生成的核心实现原理,在理解完原理后,我们也对提出的几个问题进行了探讨,相信也能够加深自己的印象。如有错误,欢迎批评指正~
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。