大炸好~我是什么也不会的MoreRoom。
今天是开门的第一篇,希望今后的日子自己能够坚持下来维护这个系列吧,争取能够让RTFSC成为一个专题。
第一篇 Java Proxy
一、简介和Demo
前段时间本来想看看Spring AOP相关的知识,顺气自然的就拐到了动态代理,关于Spring的话我准备等自认为有一些小小新的的时候再去写写吧,今天我们还是先聊一聊动态代理中的Java Proxy。
说到Java Proxy,相信大家都比较熟悉了,基于接口生成对应的代理类,进而实现代理,一般来说就是用于实现切面,应用场景就譬如说,日志打印啦,权限校验啦,事物啦等等。
具体怎么用网上有一大堆的例子,在这里也给出一个简单的例子,免去看官们一些搜demo的时间哈。
public interface IBoyService { public Boolean isBoy(); }
public interface IUserService { public String getUserName(); public String sayHello(String name); }
public class UserServiceImpl implements IUserService, IBoyService { /** * 0 = girl * 1 = boy */ private int sex; public UserServiceImpl(int sex) { this.sex = sex; } @Override public String getUserName() { System.out.println("execute method getUserName~"); return "userName"; } @Override public String sayHello(String name) { System.out.println("execute method sayHello."); System.out.println("say hello to " + name); return name; } @Override public Boolean isBoy() { System.out.println("execute method isBoy?"); return sex == 1; } }
public static void main(String[] args) { IUserService userService = new UserServiceImpl(1); IUserService proxyUserService = (IUserService) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(),userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("this is before~"); Object result = method.invoke(userService, args); System.out.println("this is after~"); return result; } }); proxyUserService.getUserName(); proxyUserService.sayHello("MoreRoom"); }
这是一个标准的代理实现,当代码执行时 返回的结果如下:
this is before~
execute method getUserName~
this is after~
this is before~
execute method sayHello.
say hello to MoreRoom
this is after~
在这里假设我们在获取接口的时候使用的工厂模式,那么就有一个很完美的时机来返回一个代理接口,当调用其中的方法时,实际上底层通过反射调用了method的invoke方法来真正的调用我们的接口,在调用invoke之前,我们想要实现什么样的业务和功能,当然是各位自己说了算啦。
二、代理类字节码
相信在我说出代理类中是通过反射实现的时候,有的同学就会问啦,为啥呀,怎么知道的呢,这里呢,只需要在Demo的main函数中加入一条配置属性:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
然后再次执行,就可以看到项目中生成了这样一个目录:com/sun/proxy
里面存放了我们生成的代理类字节码文件,$Proxy0.class,长这个样子。
public final class $Proxy0 extends Proxy implements IUserService, IBoyService {
private static Method m1;
private static Method m5;
private static Method m4;
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 Boolean isBoy() throws {
try {
return (Boolean)super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String sayHello(String var1) throws {
try {
return (String)super.h.invoke(this, m4, 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 String getUserName() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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"));
m5 = Class.forName("com.zzz.retry.test.itface.IBoyService").getMethod("isBoy");
m4 = Class.forName("com.zzz.retry.test.itface.IUserService").getMethod("sayHello", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.zzz.retry.test.itface.IUserService").getMethod("getUserName");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
大家可以简单的看一下哈,实际上没有太多难点,基本一眼略过,下面我将对源码部分进行一波讲解,受限于个人的能力,大家且看且思考吧,有不对的地方请指出探讨哈。
三、源码解析前的总结
首先我先对这次自我学习得出的结论做出一个总结。
Java Proxy是基于接口实现的动态代理,生成新的Class对象,底层业务通过反射来调用被代理的方法。而其源码的实现方面,主要是围绕着其自身的缓存来实现的。
Java Proxy的缓存为两级接口(实际上就是两层的ConcurrentHashMap),按照各位大佬的习惯,我也称之为一级缓存和二级缓存,一级缓存的Key为ClassLoader,Value为二级缓存所对应的ConcurrentHashMap。二级缓存的Key值是通过KeyFactory的Apply方法,根据ClassLoader以及接口的数量来确定的,其中接口数量为1,2,以及更多的时候都会有不同的生成方式,但是这里先不做讨论啦。二级缓存的Value则比较特殊,当缓存刚刚存入的时候,Value为Factory,Factory实现了Supplier接口,翻译为提供者,而当要从缓存中取出对应的代理类Class对象时,则调用Supplier的get方法,同时将缓存的值替换为CacheValue。
之后Proxy类获取代理Class的构造函数,生成并且返回对应的代理对象,这样一个正向的流程就完成啦,之后便着重提一下缓存的失效,说起来,既然是缓存,那么就肯定会有失效的情况。缓存类的描述,我们可以得出一级缓存的key是弱引用,是可能会被回收掉的,而二级缓存则不会,不过当一级缓存已经失效的情况下,二级缓存仿佛存在的意义也不大了。
当一级缓存的Key失效的时候,其Value,也就是二级缓存实际上依然是存在的,那么在每一次获取动态代理Class对象之前,均会将失效的一级缓存值清空,具体实现我们在看源码的过程中再聊吧。
四、源码解析
根据我们平时使用的入口。
Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
// 1. 判斷代理处理器是否存在
Objects.requireNonNull(h);
// 2. 克隆获取所有需要代理的接口
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;
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);
}
}
这里只需要留意加粗部分,那我们继续深入,先判断一下接口数量,呃。。。反正不会有这么多接口的,之后就是从缓存中获取对应的代理类Class对象了,那么,我们在进入get方法之前呢,我们要先介绍一下这个缓存。
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
这个proxyClassCache是一个私有的常量,结构为双层的ConcurrentMap(实现是用的ConcurrentHashMap),首先是声明
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
这里出现了三个没见过的类,WeakCache<K,P,V>, KeyFactory, ProxyClassFactory。
其中需要着重关注的就是WeakCache,整个缓存的逻辑都是在这个类中实现的,那么我们继续介绍一下他的结构。
// 引用队列,配合一级缓存作为弱引用使用 private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // 缓存本体没错了,两层map,第一层为一级缓存,Key=ClassLoader,value=Map。 // 二级缓存 Key为ClassLoader+interfaces value=(Factory / CacheValue) private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); // 配合业务用的临时map,基本不用关注。 private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); // Key值生成器 private final BiFunction<K, P, ?> subKeyFactory; // value生成器 private final BiFunction<K, P, V> valueFactory;
结构如上所示,其实事实上,我在注释中写的Key=ClassLoader也好 Key=ClassLoader+interfaces也罢,都不是特别准确,应该是用classsloader生成或者叫做classloader+interfaces生成吧,一级缓存的Key实际上是WeakCache中的内部类CacheKey,这个是一个弱引用,因此当没有地方引用他的话,在垃圾回收发生时,将会对其进行回收,只不过CacheKey是通过ClassLoader生成的,那么在回收之前,要首先回收ClassLoader,所以过期的可能性不是很大吧,在下面的篇幅中我会演示一下如何让缓存失效。二级缓存的Key要根据接口的数量来生成,就简单的看一下吧。
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) { 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); } }
那么我们现在进入proxyClassCache的get方法
进入后源码如下:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
// 获取一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 然后在一级缓存中查找二级缓存。 这里二级缓存可能是空。
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;
}
}
// 获取二级缓存的Key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 然后取出二级缓存的值
// 翻译为提供者
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 此循环会执行多次 因此按照标号来读
while (true) {
// B_如果提供者不是空 那么便获取代理类Class 返回
if (Supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
// A_如果工厂为空的话 则创建一个新的工厂
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
// A_二级缓存为空 那么将Factory放入缓存_此时缓存中的值是Factory
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
supplier = valuesMap.get(subKey);
}
}
}
}
中间涉及到看起来怪怪的逻辑,是因为这个缓存是个常量,那么这里面所有的操作都是暴露在多个线程之下的,不过鄙人才疏学浅,多线程的情况还是交给各位吧,我会在之后学习一下多线程来弥补一下吧。
之后呢 其中有一句话。
expungeStaleEntries();
这个就是用于清理过期缓存的,我们点进去看一下。
private void expungeStaleEntries() { CacheKey<K> cacheKey; while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) { cacheKey.expungeFrom(map, reverseMap); } }
这里用到了之前说到的引用队列,当这里的CacheKey失效的时候,则会进入refQueue,这时候通过遍历这个队列,将失效的CacheKey对应的二级缓存清空掉。
那么进入expungeFrom这个方法。
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map, ConcurrentMap<?, Boolean> reverseMap) { ConcurrentMap<?, ?> valuesMap = map.remove(this); if (valuesMap != null) { for (Object cacheValue : valuesMap.values()) { reverseMap.remove(cacheValue); } } }
这里通过判断一级缓存的Key是否与this相等(这个方法在CacheKey里面),然后通过map的remove方法来进性删除(实际是用null来进性replace),replace方法是通过判断实体的hashCode是否与原值相同,那么CacheKey便复写了hashCode方法。
@Override public int hashCode() { return hash; }
而这个hash在构造函数中赋值。
private CacheKey(K key, ReferenceQueue<K> refQueue) { super(key, refQueue); this.hash = System.identityHashCode(key); // compare by identity }
也就是当这个对象创建的一瞬间,其hash值就已经确定了,因此在缓存的Key被回收(或者说CacheKey中的reference被回收),也能够定位到对应的二级缓存来将其清理掉。
最后再放一个会出现缓存失效的代码,具体有兴趣的朋友可以贴上去执行一下。无非就是按着我上面说的执行以下而已。
public class Main { /** * 自定义的ClassLoader */ private static MClassloader classLoader = new MClassloader(); /** * 执行的main函数 * * @param args * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Main main = new Main(); main.executeProxy4GC(); Proxy.isProxyClass(new Object().getClass()); main.executeProxy(); Proxy.isProxyClass(new Object().getClass()); classLoader = null; System.gc(); Proxy.isProxyClass(new Object().getClass()); main.executeProxy(); } /** * 用于GC用的代理执行逻辑 * * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public void executeProxy4GC() throws ClassNotFoundException, IllegalAccessException, InstantiationException { classLoader.loadClass("proxy.service.IUserService"); classLoader.loadClass("proxy.service.IBoyService"); Class c = classLoader.loadClass("proxy.service.impl.UserServiceImpl"); IUserService userService = (IUserService) c.newInstance(); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(classLoader, userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(userService, args); return result; } }); } /** * 代理执行逻辑 * * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public void executeProxy() throws ClassNotFoundException, IllegalAccessException, InstantiationException { IUserService userService = new UserServiceImpl(); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(userService, args); return result; } }); } /** * 自定义的类加载器 */ private static final class MClassloader extends ClassLoader { } }
最后的最后呢,有劳各位花费时间来看一个这么冗长的帖子。有什么不对的地方也请各位指出吧。
拜谢了。