从源码角度分析JDK动态代理
前言
本篇从源码的角度,对JDK动态代理的实现,工作原理做简要分析。
一、JDK动态代理
JDK动态代理是运行时动态代理的一种实现,相比较于CGLIB ,目标对象必须实现接口,下面是一个简单案例:
接口及实现类:
public interface UserService {
void register();
}
public class UserServiceImpl implements UserService {
@Override
public void register() {
System.out.println("注册的逻辑...");
}
}
测试类:
public class ProxyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("注册前...");
Object invoke = method.invoke(userService, args);
System.out.println("完成注册...");
return invoke;
}
});
proxy.register();
}
}
关键在于Proxy.newProxyInstance
方法,该方法有三个参数:
- ClassLoader loader:指定代理类的类加载器。
- Class<?>[] interfaces:指定代理类需要实现的接口。
- InvocationHandler h:定义代理对象的行为。
最关键的是第三个参数:InvocationHandler h
官方注释给出的含义是,每次调用代理对象的方法时,都会转发到 InvocationHandler
的 invoke
方法进行处理。
案例中的代码运行后也确实是执行了invoke
的逻辑:
那么动态代理是如何生成的?调用目标类的方法,为何会执行invoke
的逻辑?下面从源码的角度进行分析
二、动态代理的生成
生成动态代理的关键,在于getProxyClass0
中的逻辑:
java.lang.reflect.Proxy#getProxyClass0
的两个参数,分别是类加载器,和代理类实现的接口。首先会尝试从proxyClassCache
缓存中获取代理类,如果获取不到,则会走get方法的逻辑进行创建:
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
//如果存在由实现给定接口的给定加载器定义的代理类,则返回缓存的副本;否则,它将通过 ProxyClassFactory 创建代理类
return proxyClassCache.get(loader, interfaces);
}
java.lang.reflect.WeakCache#get
的参数,同样是类加载器,和代理类实现的接口:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//清理缓存中已经过期或失效的条目。
expungeStaleEntries();
//将类加载器和refQueue包装成一个CacheKey对象,支持弱引用机制。
//保证即使主键对象被垃圾回收,缓存条目也能正确清理。
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
//初始化二级缓存映射
// private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//map是WeakCache的属性,value是二级缓存,每一级缓存都是ConcurrentMap的形式
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//二级缓存为空
if (valuesMap == null) {
//将一个新的 ConcurrentHashMap 放入一级缓存,使用CHM为了保证插入的并发安全,防止重复插入
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
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//获取二级缓存的value
Supplier<V> supplier = valuesMap.get(subKey);
//初始化一个工厂
Factory factory = null;
while (true) {
//如果二级缓存中已经有了value,直接获取
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
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 = new Factory(key, parameter, subKey, valuesMap);
}
// 如果当前没有 supplier
if (supplier == null) {
//将当前 Factory 插入二级缓存。
supplier = valuesMap.putIfAbsent(subKey, factory);
//supplier 为 null 证明二级缓存中没有相同的值
if (supplier == null) {
// successfully installed Factory
//当前线程成功设置factory
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);
}
}
}
}
在该方法中利用到了二级缓存
,关键在于WeakCache的map属性:一级缓存的value是一个ConcurrentMap类型的集合。
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
当某个线程第一次进入该方法时,根据key从一级缓存
中获取是为空的,就会在下面的代码片段中创建一个一级缓存
,一级缓存
中的二级缓存
此时只是初始化,是没有具体的值的:
然后会根据类加载器,和代理类实现的接口
构建一个二级缓存
的key,尝试从二级缓存
中获取值:
第一次也是获取不到的,所以会在创建Factory工厂后,通过valuesMap.putIfAbsent(subKey, factory);
去设置二级缓存
的键值:
接着第二次循环再次走到下面的代码片时,supplier已经不为空了(此时的supplier就是factory,二级缓存
的value)
不难看出上述的操作都是在为缓存赋值,使用CHM的原因也是为了防止多线程并发操作时发生重复。
接下来V value = supplier.get();
实际是调用了WeakCache
的Factory
内部类的get方法,方法上也加了锁避免并发冲突。
@Override
public synchronized V get() { // serialize access
// re-check
//从二级缓存中获取值
Supplier<V> supplier = valuesMap.get(subKey);
//还会判断supplier的类型是否被替换
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 {
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)包装生成的值 value。
CacheValue<V> cacheValue = new CacheValue<>(value);
// try replacing us with CacheValue (this should always succeed)
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
//将 cacheValue 添加到 reverseMap 中。
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
//成功生成的值(value)被缓存后,返回给调用者。
return value;
}
}
图上圈出的方法,是创建代理类的核心方法:
在java.lang.reflect.Proxy.ProxyClassFactory#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.
*/
//使用 isInterface() 确认当前类是一个接口。
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
//默认代理类为 public 和 final
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();
//如果接口不是 public,代理类必须在与接口相同的包中。
if (!Modifier.isPublic(flags)) {
//将代理类的访问权限限制为 final。不可修改
accessFlags = Modifier.FINAL;
//对于非 public 接口,提取接口的包名。
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
//所有非 public 接口在同一个包中,否则抛出异常。
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//如果所有接口都是 public,代理类定义在默认的 com.sun.proxy 包中。
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.
*/
//真正创建代理类的核心方法,偏底层的native方法
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());
}
}
}
代理类的名称格式为:[包名].Proxy $ [递增编号],为什么代理类中有$符号也是在这里决定的:
而return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
是核心中的核心,代理类正是在该方法中生成的,点进去发现是本地方法
:
那么如何才能看到在运行时动态生成的代理类?可以添加JVM参数:
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
运行项目,在当前的工作目录下即可看到运行时动态生成的代理类:
三、invoke的运行时调用
在上一步既然看到了生成的代理类,那么在调用目标方法时,为什么会转发到invoke就很清晰了:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.itbaima.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//...中间都是一些hashcode和equals的逻辑
public final void register() throws {
try {
//调用Proxy类InvocationHandler属性的invoke方法
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.itbaima.proxy.UserService").getMethod("register");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}
}
调用代理对象的 register 方法时,register 方法的调用被路由到 $Proxy0 的 register 方法,然后又会调用Proxy类InvocationHandler属性的invoke方法。
总结
- 为什么生成的代理类,方法都要被
final
修饰?
防止子类重写,绕过 InvocationHandler 的逻辑,破坏代理机制,确保代理行为的安全性。并且生成的代理类是高度封装的,设计原则是尽量减少外界对其行为的干扰。final 修饰符保证了代理类的封闭性和完整性,符合开闭原则(对扩展开放,对修改封闭)。 - JDK 动态代理中的 WeakCache为什么要使用
二级缓存
机制?
一级缓存
存储了一个 Supplier,用来提供对代理类的引用。二级缓存
存储实际生成的代理类。二级缓存的存在可以保存生成的代理类,避免因一级缓存
中的数据被回收导致代理类被重复生成。并且二级缓存
通过弱引用管理代理类生命周期,避免内存泄漏。
原文地址:https://blog.csdn.net/2301_77599076/article/details/143822396
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!