Java 动态代理详解
在Java中,可以采用如下的方式进行动态代理
先定义一个接口A
再定义一个它的实现
继续定义一个代理类,主要注意其实现InvocationHandler
在main中可以这样进行实现:
执行结果如下:
所以问题来了:为什么可以将这个这个ProxyA的实例给绑定a进行强制转换呢?它究竟是个啥?
其实是动态生成了一个class,这个class中的内容类似如上图(借用图),可以看出这个动态生成的class,本质上实现了functionA的方法
那么第二个问题是这个文件是怎么生成的呢?这就要看newProxyInstance方法了,首先可以看到这个方法上有个注解@CallerSensitive
这个注解是JVM专用的注解,用于找到真正发起反射请求调用的类
大牛说: “这个注解是为了堵住漏洞用的。曾经有黑客通过构造双重反射来提升权限,原理是当时反射只检查固定深度的调用者的类,看它有没有特权,例如固定看两层的调用者(getCallerClass(2))。如果我的类本来没足够权限群访问某些信息,那我就可以通过双重反射去达到目的:反射相关的类是有很高权限的,而在 我->反射1->反射2 这样的调用链上,反射2检查权限时看到的是反射1的类,这就被欺骗了,导致安全漏洞。使用CallerSensitive后,getCallerClass不再用固定深度去寻找actual caller(“我”),而是把所有跟反射相关的接口方法都标注上CallerSensitive,搜索时凡看到该注解都直接跳过,这样就有效解决了前面举例的问题”
讲的有点云里雾里,我的理解是:如果没有这个注解,那我->反射1->反射2 ,这个时候反射2 智能看到反射1 ,误以为我也有和1 相同的权限,会出问题,有了这个注解之后,我->反射1 的时候会给反射1 上面的方法加上这个注解,反射2找自己的调用者的时候,就可以直接略过带这个注解的,就能正确的找到我,然后判断是否有权限。
这个方法内部主要的代码如下:
主要是先clone接口,然后验证权限,然后获得动态生成的class,再调用这个class的构造函数生成实例
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
Class<?> cl = getProxyClass0(loader, intfs);
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;
}
});
}
return cons.newInstance(new Object[]{h});
主要关注getProxyClass0方法,内部如下:
可以看到是在缓存中取出这个对象的,对象的类型为ProxyClassFactory
在这个factory中生成真正的class文件