文章目录
- 前言
- 一、JDK动态代理
- 二、动态代理的生成
- 三、invoke的运行时调用
- 总结
前言
本篇从源码的角度,对JDK动态代理的实现,工作原理做简要分析。
一、JDK动态代理
JDK动态代理是运行时动态代理的一种实现,相比较于CGLIB ,目标对象必须实现接口,下面是一个简单案例:
接口及实现类:
public interface UserService {void register();
}
public class UserServiceImpl implements UserService {@Overridepublic 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() {@Overridepublic 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//利用类加载器和和代理类实现的接口生成一个二级缓存keyObject subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));//获取二级缓存的valueSupplier<V> supplier = valuesMap.get(subKey);//初始化一个工厂Factory factory = null;while (true) {//如果二级缓存中已经有了value,直接获取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 Factoryif (factory == null) {//懒加载一个 Factoryfactory = new Factory(key, parameter, subKey, valuesMap);}// 如果当前没有 supplierif (supplier == null) {//将当前 Factory 插入二级缓存。supplier = valuesMap.putIfAbsent(subKey, factory);//supplier 为 null 证明二级缓存中没有相同的值if (supplier == null) {// successfully installed Factory//当前线程成功设置factorysupplier = factory;}// else retry with winning supplier} else {if (valuesMap.replace(subKey, supplier, factory)) {// successfully replaced// cleared CacheEntry / unsuccessful Factory// with our Factorysupplier = factory;} else {// retry with current suppliersupplier = 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方法,方法上也加了锁避免并发冲突。
@Overridepublic 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 loopreturn null;}// else still us (supplier == this)// create new valueV value = null;try {value = Objects.requireNonNull(valueFactory.apply(key, parameter));} finally {if (value == null) { // remove us on failurevaluesMap.remove(subKey, this);}}// the only path to reach here is with non-null valueassert 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
方法中,首先会进行接口的各种校验,以及生成代理类的名称
@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");}/** 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 和 finalint 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 packageproxyPkg = 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,用来提供对代理类的引用。二级缓存
存储实际生成的代理类。二级缓存的存在可以保存生成的代理类,避免因一级缓存
中的数据被回收导致代理类被重复生成。并且二级缓存
通过弱引用管理代理类生命周期,避免内存泄漏。