Java原始碼分析——java.lang.reflect反射包解析(三) 動態代理、Proxy類、WeakCache類
代理模式是一個經常被各種框架使用的模式,比如Spring AOP、Mybatis中就經常用到,當一個類訪問另外一個類困難時,可通過一個代理類來間接訪問,在Java中,為了保證程式的簡單性,代理類與目標類需要實現相同的介面。也就是說代理模式起著一箇中轉站的作用,如圖:
用一個簡單的例子來描述代理模式:
public interface Catch{
void print();
}
public class Cat implements Catch{
@Override
public void print() {
System.out.println("Cat 開始抓老鼠");
}
}
public class ProxyClassTest implements Catch{
private Catch aCatch;
public ProxyClassTest(Catch aCatch){
this.aCatch=aCatch;
}
@Override
public void print() {
System.out.println("dog請求cat抓老鼠" );
aCatch.print();
}
}
public class Dog {
public static void main(String args[]) {
ProxyClassTest test=new ProxyClassTest(new Cat());
test.print();
}
}
從上述程式碼可以看出,當dog向代理類提出請求抓老鼠後,原本請求類(dog)便可以不管了,而代理類則通過自己的方式找到可以實現dog的請求的類或方法,然後執行完成dog的請求。代理模式的好處也可以得出了,就是原本請求類是可以不關注它的請求行為是如何具體的實現的,減少了程式碼間的耦合度。
WeakCache類
在討論Proxy類之前,不得不先了解這個類,這個類是用來幹嘛的呢?從其英文意義上來看,它是弱快取類,也就是說它是一個起著快取作用的類,它用來存貯軟引用型別的例項,從該類在Proxy類中的例項化可以看出:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
從上述程式碼中可以看出,WeakCache是快取類載入器以及該載入器載入的委託類的。其中KeyFactory類用來生產Key的,生成因不同數量的類載入器採取不同的生成策略來對應生成的Key物件,而其實現如下:
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
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);
}
}
}
private static final class Key1 extends WeakReference<Class<?>> {
private final int hash;
Key1(Class<?> intf) {
super(intf);
this.hash = intf.hashCode();
}
......
}
在這裡列出了Key1類的實現,從其原始碼中看出,該類繼承了WeakReference類,也就是弱引用類,而每個Key類具體用來幹嘛的呢?其實就是作為鍵的生成策略存在,保證其鍵的唯一性,而KeyFactory工廠類根據類載入器數量的不同採取鍵的不同生成策略。後者ProxyClassFactory工廠類則負責驗證其裡面的委託介面是否已經載入過、是否是介面、驗證其訪問許可權、驗證包的資訊與判定,最後通過defineClass方法實現類(廣義上介面也是類)的載入,也就是把.class檔案轉變成對應介面的Class類物件:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
......
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
......
//最後呼叫了native本地方法實現其Class物件的獲取
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
......
}
}
也就是說,WeakCache類的實質作用是快取實現了代理目標介面的類的資訊以及對這些類的一些操作,以及這些類的類載入器的。其中的重要的方法是get方法,該方法是來獲取WeakCache快取中與委託類的Class類物件的。這句話什麼意思呢?就拿上文的代理模式的例子來說,這個代理目標介面實現的類就是Cat類,也就是委託類,而代理類是ProxyClassTest類。WeakCache重要的方法是,get方法,該方法實現了快取的機制,其原始碼以及註釋如下:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//刪除過期的快取
expungeStaleEntries();
//建立CatchKey物件,Key不為空
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 懶惰地為特定的CaseKeKE安裝第二級值對映
//根據CatchMap獲取ConcurrentMap物件
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//如果沒有則建立一個ConcurrentMap物件
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//利用KeyFactory獲取Key物件
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//利用Key物件取出Supplier,Supplier是一個介面
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
//不為空,則直接取出
if (supplier != null) {
// 從supplier介面的實現類獲取Class類物件
V value = supplier.get();
if (value != null) {
return value;
}
}
//如果supplier為空,進行快取,Factory是supplier介面的實現類
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//賦值factory
supplier = factory;
}
} else {
//假如不為空,且factory中value為空,則替換factory
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
//繼續嘗試獲取介面實現類的supplier類物件
supplier = valuesMap.get(subKey);
}
}
}
}
get方法利用ConcurrentMap類來進行快取,裡面存貯Key物件以及Factory物件,Factory類用來存貯快取的資訊的,其實現了Supplier介面,其get方法裡面呼叫了ProxyClassFactory類的apply方法來返回Class類物件,而且get方法是一個同步的方法,也表明WeakCache快取是執行緒安全的。
Proxy類
回到我們的主題,也就是Proxy類,這是Java官方提供的一個代理類,用來實現代理模式,而其提供的是動態的代理,而上述貓抓老鼠的例子是靜態的代理,兩者之間有什麼區別呢?靜態代理的代理類雖然減少了程式碼間的耦合度,但是有如下的缺點:
- 代理類和委託類實現了相同的介面,代理類通過委託類實現了相同的方法。這樣就出現了大量的程式碼重複。如果介面增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了程式碼維護的複雜度。
- 代理物件只服務於一種型別的物件,沒有對多種物件的代理。
而動態代理則消除掉了這些弊端,Java為動態代理提供了一個介面,該介面是代理類需要實現的:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy是代理類的物件,method是委託者被代理的方法的物件,args指代理類例項上方法呼叫的引數。
Proxy類中主要的方法是檢查許可權類以及建立代理類的例項類,因為Proxy沒有公共的構造方法,所以只能通過newProxyInstance方法來建立代理類的例項,這個代理類的例項並不是Proxy類的例項,這個需要注意:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//InvocationHandler介面實現類不為空
Objects.requireNonNull(h);
//克隆介面
final Class<?>[] intfs = interfaces.clone();
//進行類載入器的許可權判斷
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//獲取WeakCache類快取的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;
}
});
}
//建立對應InvocationHandler實現類的例項
return cons.newInstance(new Object[]{h});
......
}
通過一個例子來加深理解:
public interface Catch{
void print(String mouse);
}
public class Cat implements Catch{
@Override
public void print(String mouse) {
System.out.println("Cat 開始抓"+mouse);
}
}
public class ProxyClassTest implements InvocationHandler{
private Object oclass;
public Object ProxyClassInstance(Object oclass){
this.oclass=oclass;
return Proxy.newProxyInstance(oclass.getClass().getClassLoader(),oclass.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(oclass,args);
return null;
}
}
public class Test {
public static void main(String args[]) {
Catch test= (Catch) new ProxyClassTest().ProxyClassInstance(new Cat());
test.print("老鼠");
}
}
//列印:Cat 開始抓老鼠
利用Java實現動態代理類,需要實現InvocationHandler介面,重寫裡面的invoke方法,並由Proxy類newProxyInstance方法來建立委託類的例項。從程式碼中看出,代理類並不需要依賴委託類,消除了靜態代理的弊端。