概述
jdk动态代理是基于接口的代理,其在运行期间创建对应接口的代理类字节码class并将字节码class装载进虚拟机实现代理。
简单jdk动态代理例子
我们通过对代理对象创建代理类后,可以在代理对象的被代理方法执行前后增加额外的逻辑,可以修改被代理方法的参数和执行返回结果,甚至可以控制被代理方法是否执行。
public class JdkProxyTest {
public static void main(String[] args) {
Itf proxy = (Itf) Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), new Class[]{Itf.class}, new MyInvocationHandler(new ItfImpl()));
Object res = proxy.fun1("测试参数");
System.out.println(res);
}
}
interface Itf {
Object fun1(Object param);
}
class ItfImpl implements Itf {
@Override
public Object fun1(Object param) {
System.out.println("ItfImpl->fun1() executing···,输入参数是:"+param);
return "测试结果";
}
}
class MyInvocationHandler implements InvocationHandler {
private Itf itf;
public MyInvocationHandler(Itf itf) {
this.itf = itf;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在方法:" + method.getName() + " 执行前织入其它逻辑···");
Object invoke = method.invoke(itf, args[0]+ " 修改原方法参数");
System.out.println("do sth after method:" + method.getName() + " executing···");
System.out.println("在方法:" + method.getName() + " 执行后织入其它逻辑···");
return invoke + " 修改原方法执行结果";
}
}
测试结果如下:
在方法:fun1 执行前织入其它逻辑···
ItfImpl->fun1() executing···,输入参数是:测试参数 修改原方法参数
do sth after method:fun1 executing···
在方法:fun1 执行后织入其它逻辑···
测试结果 修改原方法执行结果
添加VM参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true可以将生成的动态代理类输出到文件,这个文件默认处于当前项目的jdk.proxy包下:本测试项目生成代理类如下:
package jdk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Itf {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(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 Object fun1(Object var1) throws {
try {
return (Object)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("jdk.proxy.Itf").getMethod("fun1", Class.forName("java.lang.Object"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到生成的代理类默认继承了Proxy类并实现了相关代理接口,这也是为什么jdk动态代理只能代理接口的原因(Java单继承),至于为什么要继承Proxy类,从生成的代理中可以看到除了使用super().h.invoke···之外没有使用到Proxy类中的其它方法,因此我断定原因就是乌龟的屁股:龟腚。
此外所有的代理的接口的方法都通过静态成员变量标识,避免反射获取方法降低效率,hashCode,equals,toString均被固定代理。
构造方法中传入一个InvocationHandler最终被赋值给Proxy的h变量,这也是继承Proxy的唯一使用地方
源码分析
jdk创建代理类通过Proxy#newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法实现,该方法要求传入3个参数
- loader:构造代理类的类加载器
- interfaces:被代理类实现的一系列接口
- h:InvocationHandler的实例,实现具体的代理逻辑
@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);
}
//创建代理类的Class对象,即创建字节码信息并加载到虚拟机中
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取构造器方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//如果构造器方法的访问权限不是public,反射设置其为可访问状态
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方法中,该方法如下:
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创建代理类缓存后并返回
//其中proxyClassCache被默认初始化为:new WeakCache<>(new KeyFactory(), new ProxyClassFactory())
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache.get(loader, interfaces)方法逻辑如下
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//清理缓存,使用Map缓存,其key使用WeakReference,因此key可能会被垃圾回收器回收,当key被回收后(放入referenceQueque中)通过该key回收其value
expungeStaleEntries();
//构建可被自动回收的cacheKey(主要方式代理类过多占用内存),这里传入的key是ClassLoader,因此cacheKey作为一级缓存以类加载器作为区分
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
//先判断缓存map是否存在,如果不存在则初始化缓存value map
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
//使用keyFactory构建subkey,这里有点绕,简单来说cacheKey作为一级映射获取key获取该类加载器下的缓存map,再通过subKey(二级缓存key,类加载器和对应代理接口)获取最终的缓存信息,最终的缓存信息是一个函数式接口,通过该接口获取代理类实例Class
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
//通过函数接口获取Class对象,这里supplier有两种实现,首次创建代理类时为Factory类的实例,创建好后使用CacheKey封装代理Class对象替换Factory实例,其中Factory和Cache都是Supplier的函数接口
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
//初次调用或由于cacheKey被回收后都会触发factory的新构建
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
//添加到二级缓存,此时需要重新生成字节码
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
//在此期间cacheKey过期但缓存没有被回收则进行替换操作
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);
}
}
}
}
private void expungeStaleEntries()主要用于清理陈旧的缓存信息
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
//通过循环遍历refQueue来清理map和reverseMap,refQueue是由虚拟机进行维护的,详情参见java Reference
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
cacheKey.expungeFrom(map, reverseMap);
}
}
void cacheKey.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)...
//当cacheKey被虚拟机自动回收后,则再调用该方法时将清理掉对应的value
ConcurrentMap<?, ?> valuesMap = map.remove(this);
// remove also from reverseMap if needed
if (valuesMap != null) {
for (Object cacheValue : valuesMap.values()) {
reverseMap.remove(cacheValue);
}
}
}
上述代码中创建subKey通过:subKeyFactory.apply(key, parameter),subKeyFactory就是KeyFactory,其apply方法如下:
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
//简单判断接口数量根据接口数量构建Key,注意Key1,Key2,Keyx用于保存接口的类都是弱引用类
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
最终通过Factory类(函数式接口实现)来获取Class类信息,源码如下:
@Override
public synchronized V get() { // serialize access
// re-check
//重复检测判断缓存map中缓存的Supplier和当前Supplier是否是同一对象
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
//多线程或者由于JVM不定时回收可能会导致该种情况,此时将返回null进入上层循环重新获取Supplier后再执行该方法
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
//valueFactory即ProxyClassFactory,通过该BiFunction函数接口获取Class代理对象
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)
//将创建好的Class代理对象封装成CacheValue对象
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
//reverseMap主要用于判断一个类是否是jdk的动态代理以及统计jdk动态代理类数量
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
//将CacheValue对象替换Factory对象,之后再代理时就不用再次构建字节码信息并装载了
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;
}
最终创建Class对象的逻辑处于ProxyClassFactory中,具体如下:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//for循环遍历代理接口做检测,接口匹配对应类加载器、是同一接口
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();
//如果存在非public接口,则检测代理接口是否位于同一包下,不处于同一包下将抛出异常
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");
}
}
}
//默认包使用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.
*/
//创建代理名,使用$Proxy0,$Proxy1依次类推
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
//使用sun提供的字节码操纵框架构建class的字节码信息
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//使用类加载器装载字节码生成Class对象并返回
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());
}
}
总结
- jdk的动态代理类使用了弱缓存,即首次构建Class字节码后会进行缓存之后再次创建代理时则直接使用缓存即可,但由于是若缓存原因代理Class会被JVM根据需要回收,因此被回收后需要再次构建
- 缓存结构为一个ConcurrentMap<Object, ConcurrentMap<Object, Supplier>> map的二级缓存结构,key为CacheKey类型,其继承了WeakReference,因此会被JVM根据需要回收,同时key作为一级缓存主要用来标记类加载器,因此通过该key获取的一级缓存为指定类加载的二级缓存
- 二级缓存的key通过类加载器和代理接口共同标记,其类型是new Object()或Key1(代理一个接口),Key2(代理2个接口),KeyX(代理多个接口)均是弱引用类型,因此可以指向固定的一组接口的代理类。
- 二级缓存的其Value是一个Supplier函数式接口,首次创建一组接口的代理类时由于其字节码未生成因此Supplier=Factory,创建好字节码后Supplier会使用CacheValue封装Class对象替换Factory,Factory和CacheValue都是实现了Supplier接口。
- 一级缓存的key、二级缓存的key、二级缓存的value(生成字节码后的封装成的CacheValue)均使用了弱引用,其中只有一级缓存使用了ReferenceQueue,因此最终只有一级缓存的引用会被加入到ReferenceQueue中被手动清除。查看二级缓存的key、二级缓存的value的hashCode和equals方法均使用引用对象进行计算,因此同一对象的不同二级缓存key或二级缓存Value其值相等。