Java动态代理Proxy类源码分析
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{ // 验证传入的InvocationHandler不能为空,为空就抛出NullPointerException 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); try { if (sm != null) { //权限检查 checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 从代理类对象中查找参数为InvocationHandler的代理类构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; // 检测构造器是否是Public修饰,如果不是则强行转换为可以访问的。 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedActionnewProxyInstance方法首先是对参数进行一些权限校验,之后通过调用getProxyClass0方法生成了代理类的类对象,然后获取参数类型是InvocationHandler.class的代理类构造器。检验构造器是否可以访问,最后传入InvocationHandler实例的引用去构造出一个代理类实例。 newProxyInstance是Proxy的静态方法,代码并不难理解,除去权限检查的代码外,就剩下两步:() { public Void run() { cons.setAccessible(true); return null; } }); } // 传入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、获取代理类对象(Class<?> cl = getProxyClass0(loader, intfs))
- 2、利用反射技术实例化代理类,并返回实例化对象(return cons.newInstance(new Object[]{h}))
/**
* 生成一个代理类对象,
*/
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
// 接口类对象数组不能大于65535个,否则抛出异常
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 从代理类对象缓存中,根据类加载器和接口类对象数组查找代理类对象
return proxyClassCache.get(loader, interfaces);
}
在这个方法中,是直接从一个叫proxyClassCache缓存中读取的, 如果根据提供的类加载器和接口数组能在缓存中找到代理类就直接返回该代理类。如果没有在缓存中找到,会调用ProxyClassFactory工厂去生成代理类。很明显ProxyClassFactory这个工厂就是用来生成代理类对象!
这里用到的缓存是二级缓存,它的一级缓存key是根据类加载器生成的,二级缓存key是根据接口数组生成的。来看一下这个缓存的声明:private static final WeakCache[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
这里涉及到三个类:WeakCache 、KeyFactory、ProxyClassFactory,其中后面两个类都是Proxy类的静态内部类,keyFactory是用来生产key的,ProxyClassFactory是用来生产代理类对象的,这个稍后会提到。
看一下WeakCache的大致结构:final class WeakCache{ //Reference引用队列 private final ReferenceQueue refQueue= new ReferenceQueue<>(); // 缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object private final ConcurrentMap
上面的源代码中写明, WeakCache缓存的内部实现是通过ConcurrentMap来完成的,成员变量map就是二级缓存的底层实现,reverseMap是为了实现缓存的过期机制,subKeyFactory是二级缓存key的生成工厂,通过构造器传入,这里传入的值是Proxy类的KeyFactory,valueFactory是二级缓存value的生成工厂,通过构造器传入,这里传入的是Proxy类ProxyClassFactory。
代理类对象是从proxyClassCache中读取的,接下来就分析WeakCache的get方法: 4、WeakCache.get方法// key是类加载器,parameter为接口类对象数组
public V get(K key, P parameter) {
// 接口类对象数组null检查。
Objects.requireNonNull(parameter);
//清除过期的缓存
expungeStaleEntries();
// 生成缓存key对象实例,如果key = null,cacheKey = new Object();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 获得二级缓存
ConcurrentMap
WeakCache的get方法并没有用锁进行同步,那它是怎样实现线程安全的呢?因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能。我们看到ClassLoader作为了一级缓存的key,这样可以首先根据ClassLoader筛选一遍,因为不同ClassLoader加载的类是不同的。然后它用接口数组来生成二级缓存的key,这里它进行了一些优化,因为大部分类都是实现了一个或两个接口,所以二级缓存key分为key0,key1,key2,keyX。key0到key2分别表示实现了0到2个接口,keyX表示实现了3个或以上的接口,事实上大部分都只会用到key1和key2。这些key的生成工厂是在Proxy类中,通过WeakCache的构造器将key工厂传入。这里的二级缓存的值是一个Factory实例,最终代理类的值是通过Factory这个工厂来获得的。
5、Factory工厂 那么接下来,就分析一下Factory.get方法。Factory类是WeakCache的内部类。这个类中除了构造方法外,就是get方法了,下面是这个类的代码:public class Factory {
//一级缓存key, 根据ClassLoader生成
private final K key;
// 代理类实现的接口数组
private final P parameter;
// 二级缓存key, 根据接口数组生成
private final Object subKey;
//二级缓存
private final ConcurrentMap> valuesMap;
Factory(K key, P parameter, Object subKey, ConcurrentMap> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() {
//这里再一次去二级缓存里面获取Supplier, 用来验证是否是Factory本身
Supplier supplier = valuesMap.get(subKey);
if (supplier != this) {
//在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试
//期间supplier可能替换成了CacheValue, 或者由于生成代理类失败被从二级缓存中移除了
return null;
}
V value = null;
try {
//委托valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
//如果生成代理类失败, 就将这个二级缓存删除
if (value == null) {
valuesMap.remove(subKey, this);
}
}
//只有value的值不为空才能到达这里
assert value != null;
// 使用弱引用包装生成的代理类
CacheValue cacheValue = new CacheValue<>(value);
// 将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错
if (valuesMap.replace(subKey, this, cacheValue)) {
//将cacheValue成功放入二级缓存后, 再对它进行标记
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
//最后返回没有被弱引用包装的代理类
return value;
}
}
Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步。进行get方法后首先会去验证subKey对应的suppiler是否是工厂本身,如果不是就返回null,而WeakCache的get方法会继续进行重试。如果确实是工厂本身,那么就会委托ProxyClassFactory生成代理类,ProxyClassFactory是在构造WeakCache的时候传入的。所以这里解释了为什么最后会调用到Proxy的ProxyClassFactory这个内部工厂来生成代理类。生成代理类后使用弱引用进行包装并放入reverseMap中,最后会返回原装的代理类。
关键代码:valueFactory.apply(key, parameter) 注意这里的valueFactory就是Proxy的静态内部类ProxyClassFactory,上面也提到过,接着分析ProxyClassFactory的apply方法。 6、ProxyClassFactory (apply方法)/**
* 一个利用给定的类加载器和接口类数组生成,定义并返回代理类对象的工厂方法
*/
private static final class ProxyClassFactory
implements BiFunction[], Class<?>>
{
// 所有代理类对象的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 用原子类来生成代理类的序号,用于生成唯一代理类名称
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//
for (Class<?> intf : interfaces) {
/*
*这里遍历interfaces数组进行验证, 主要做三件事情
*1.intf是否可以由指定的类加载进行加载
*/
Class<?> interfaceClass = null;
try {
// 加载接口类,获得接口类的类对象,第二个参数为false表示不进行实例化
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 验证interfaceClass是否是一个接口.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 3、interfaceClass在数组中是否有重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
// 生成代理类的包名
String proxyPkg = null;
//生成代理类的访问标志, 默认是public final的
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
for (Class<?> intf : interfaces) {
//获取接口的权限修饰符
int flags = intf.getModifiers();
//如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
if (!Modifier.isPublic(flags)) {
//生成的代理类的访问标志设置为final
accessFlags = Modifier.FINAL;
//获取接口全限定名, 例如:java.util.Collection
String name = intf.getName();
int n = name.lastIndexOf('.');
//剪裁后得到包名:java.util
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");
}
}
}
//如果接口访问标志都是public的话,
if (proxyPkg == null) {
// 生成的代理类都放到默认的包下:com.sun.proxy
proxyPkg = com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//生成代理类的序号
long num = nextUniqueNumber.getAndIncrement();
//生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成代理类class文件
//核心地方,用ProxyGenerator来生成字节码
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());
}
}
}
}
在代码的 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);,生成了代理类的class文件,并且return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);返回了我们需要的代理类对象。
通过指定的 ClassLoader 和 接口数组 用工厂方法生成 proxy class。 然后这个 proxy class 的名字是:private static final String proxyClassNamePrefix = "$Proxy"; long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;
所以,动态生成的代理类名称是包名+$Proxy+id序号。
生成过程的核心代码如下:byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
上述代码中可以看到,生成了代理类的class文件,并且返回了我们需要的代理类对象。那么怎么找到这个生成的代理类class文件呢。
其实动态生成的 proxy class文件 与 Proxy 这个类在同一个包。 从上面可以看到代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成代理类的字节码。 ProxyGenerator这个类存放在sun.misc包下,我们可以通过OpenJDK源码来找到这个类,该类的generateProxyClass()静态方法的核心内容就是去调用generateClassFile()实例方法来生成Class文件。我们直接来看generateClassFile()这个方法内部做了些什么。 7、ProxyGenerator类generateProxyClassFile方法public class ProxyGenerator {
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象
//首先为代理类生成toString, hashCode, equals等代理方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//对于具有相同签名的代理方法, 检验方法的返回值是否兼容
for (List sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
try {
//添加构造器方法
methods.add(generateConstructor());
//遍历缓存中的代理方法
for (List sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//添加代理类的静态字段, 例如:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//添加代理类的代理方法
methods.add(pm.generateMethod());
}
}
//添加代理类的静态字段初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//验证方法和字段集合不能大于65535
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步, 写入最终的class文件
//验证常量池中存在代理类的全限定名
cp.getClass(dotToSlash(className));
//验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
cp.getClass(superclassName);
//验证常量池存在代理类接口的全限定名
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//接下来要开始写入文件了,设置常量池只读
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
//1.写入魔数
dout.writeInt(0xCAFEBABE);
//2.写入次版本号
dout.writeShort(CLASSFILE_MINOR_VERSION);
//3.写入主版本号
dout.writeShort(CLASSFILE_MAJOR_VERSION);
//4.写入常量池
cp.write(dout);
//5.写入访问修饰符
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
//6.写入类索引
dout.writeShort(cp.getClass(dotToSlash(className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
dout.writeShort(cp.getClass(superclassName));
//8.写入接口计数值
dout.writeShort(interfaces.length);
//9.写入接口集合
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
//10.写入字段计数值
dout.writeShort(fields.size());
//11.写入字段集合
for (FieldInfo f : fields) {
f.write(dout);
}
//12.写入方法计数值
dout.writeShort(methods.size());
//13.写入方法集合
for (MethodInfo m : methods) {
m.write(dout);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//转换成二进制数组输出
return bout.toByteArray();
}
}
从上面代码中看到generateClassFile()方法是按照Class文件结构进行动态拼接的。
什么是Class文件呢?在这里我们先要说明下,我们平时编写的Java文件是以.java结尾的,在编写好了之后通过编译器进行编译会生成.class文件,这个.class后缀结尾的文件就是Class文件。Java程序的执行只依赖于Class文件,和Java文件没有关系。 这个Class文件描述了一个类的信息,当我们需要使用到一个类时,Java虚拟机就会提前去加载这个类的Class文件并进行初始化和相关的检验工作,Java虚拟机能够保证在你使用到这个类之前就会完成这些工作,我们只需要安心的去使用它就好了,而不必关心Java虚拟机是怎样加载它的。当然,Class文件并不一定非得通过编译Java文件而来,目前JVM就可以支持Kotlin编译出来的class文件。在这里,JDK动态代理就是通过程序来动态生成Class文件的,再回到上面的代码中,可以看到,生成Class文件主要分为三步: 第一步:收集所有要生成的代理方法,将其包装成ProxyMethod对象并注册到Map集合中。 第二步:收集所有要为Class文件生成的字段信息和方法信息。 第三步:完成了上面的工作后,开始组装Class文件。 一个类的核心部分就是它的字段和方法。我们重点聚焦第二步,看看它为代理类生成了哪些字段和方法。在第二步中,按顺序做了下面四件事。 1.为代理类生成一个带参构造器,传入InvocationHandler实例的引用并调用父类的带参构造器。 2.遍历代理方法Map集合,为每个代理方法生成对应的Method类型静态域,并将其添加到fields集合中。 3.遍历代理方法Map集合,为每个代理方法生成对应的MethodInfo对象,并将其添加到methods集合中。 4.为代理类生成静态初始化方法,该静态初始化方法主要是将每个代理方法的引用赋值给对应的静态字段。 通过以上分析,可以写出JDK动态代理最终生成如下结构的代理类(类文件在动态代理语法中): 下面的代码是动态代理开发的的流程://抽象主题
interface AbstractSubject
{
void request();
}
//真实主题
class RealSubject implements AbstractSubject
{
public void request()
{
System.out.println("访问真实主题方法...");
}
}
//真实主题
class RealSubject1 implements AbstractSubject
{
public void request()
{
System.out.println("访问真实主题方法1...");
}
}
//动态代理类
class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(object, args);
after();
return result;
}
private void before() {
System.out.println("hello!");
}
private void after() {
System.out.println("bye!");
}
}
//测试类
public class TestDynamicProxyPattern {
public static void main(String[] args) {
AbstractSubject abstractSubject = new RealSubject();
DynamicProxy dynamicProxy = new DynamicProxy(abstractSubject);
Proxy.newProxyInstance(abstractSubject.getClass().getClassLoader(), abstractSubject.getClass().getInterfaces() , dynamicProxy);
abstractProxy.request();
}
}
下面的代码是JDK动态代理最终生成如下结构的代理类如下:
//JDK动态代理最终会为我们生成如下结构的代理类
class Proxy0 extends Proxy implements AbstractSubject {
//第一步生成构造器函数
protected Proxy0(InvocationHandler h) {
super(h);
}
//第二步, 生成静态域
private static Method m1; //hashCode方法
private static Method m2; //equals方法
private static Method m3; //toString方法
private static Method m4; //抽象接口中的request方法
//第三步, 生成代理方法(四个)
@Override
public int hashCode() {
try {
return (int) h.invoke(this, m1, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public boolean equals(Object obj) {
try {
Object[] args = new Object[] {obj};
return (boolean) h.invoke(this, m2, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public String toString() {
try {
return (String) h.invoke(this, m3, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void request() {
try {
//构造参数数组, 如果request方法有多个参数,直接向{}里面添加参数就行了
Object[] args = new Object[] {};
h.invoke(this, m4, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
//第四步, 生成静态初始化方法
static {
try {
Class c1 = Class.forName(Object.class.getName());
Class c2 = Class.forName(AbstractSubject.class.getName());
m1 = c1.getMethod("hashCode", null);
m2 = c1.getMethod("equals", new Class[]{Object.class});
m3 = c1.getMethod("toString", null);
//注意参数request方法有多个参数,直接向{}里面添加参数就行了,因为request方法中没有参数,直接传一个null就行
m4 = c2.getMethod("save", new Class[]{});
//...
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TestDynamicProxyPattern {
public static void main(String[] args) {
Object object = new RealSubject();
DynamicProxy dynamicProxy = new DynamicProxy(object);
AbstractSubject abstractSubject = new Proxy0(dynamicProxy);
abstractSubject.request;
}
}
经过层层分析,还原了动态生成的代理类的本来面:
1.代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。 2.代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。 3.调用invoke()方法时会传入代理实例本身,目标方法和目标方法参数。解释了invoke()方法的参数是怎样来的。 参考:https://www.cnblogs.com/liuyun1995/p/8144706.html