1 動(dòng)態(tài)代理使用
先看下動(dòng)態(tài)代理如何使用落午,然后再分析下實(shí)現(xiàn)原理
Object proxy = Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
String name = method.getName();
Log.log("Object o, Method method, Object[] objects----------");
if (method.getName().equals("setName")) {
String item=(String) objects[0];
objects[0]="modify="+item;
}
return method.invoke(person, objects);
}
});
jdk1.8之前動(dòng)態(tài)代理在實(shí)現(xiàn)時(shí)反射會(huì)被頻繁調(diào)用到谎懦,所以在性能上會(huì)稍微差一些,但在jdk1.8對(duì)動(dòng)態(tài)代理的實(shí)現(xiàn)做了改良溃斋,性能有所提高
2 JDK 1.7動(dòng)態(tài)代理實(shí)現(xiàn)
看下在JDK1.7中動(dòng)態(tài)代理的實(shí)現(xiàn)邏輯界拦,大致如下:
根據(jù)classloader和動(dòng)態(tài)代理的接口類型先從緩存中獲取已經(jīng)生成的class對(duì)象,如果存在該對(duì)象則拿到該對(duì)象后通過反射生成代理對(duì)象梗劫。如果該class對(duì)象不存在則通過ProxyGenerator的generateProxyClass方法創(chuàng)建出對(duì)應(yīng)的byte數(shù)組
最終調(diào)用defineclass方法將byte數(shù)組轉(zhuǎn)換成class對(duì)象并緩存下次使用享甸。
源碼分析:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
...
// 1、通過 loader 和 interfaces 創(chuàng)建動(dòng)態(tài)代理類
Class<?> cl = getProxyClass0(loader, interfaces);
try {
// 2梳侨、通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù)(參數(shù)類型是 InvocationHandler.class 類型)
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
...
// 3蛉威、通過動(dòng)態(tài)代理類的構(gòu)造函數(shù)和調(diào)用處理器對(duì)象創(chuàng)建代理類實(shí)例
return newInstance(cons, ih);
...
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
調(diào)用getProxyClass0,如果緩存存在則直接使用走哺,沒有緩存則內(nèi)部創(chuàng)建瓷翻。拿到class對(duì)象后通過反射創(chuàng)建代理對(duì)象。
getProxyClass0整體邏輯如下割坠,先添個(gè)整體代碼,后面分段看具體邏輯:
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
Class<?> proxyClass = null;
// 接口名稱數(shù)組妒牙,用于收集接口的名稱作為代理類緩存的 key
String[] interfaceNames = new String[interfaces.length];
// 接口集合彼哼,用于檢查是否重復(fù)的接口
Set<Class<?>> interfaceSet = new HashSet<>();
// 遍歷目標(biāo)對(duì)象實(shí)現(xiàn)的接口
for (int i = 0; i < interfaces.length; i++) {
// 獲取接口名稱
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
// 通過反射加載目標(biāo)類實(shí)現(xiàn)的接口到內(nèi)存中
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
...
// 如果接口重復(fù),拋出異常
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
// 將接口名稱數(shù)組轉(zhuǎn)換為接口名稱列表
List<String> key = Arrays.asList(interfaceNames);
// 通過 Classloader 獲取或者創(chuàng)建一個(gè)代理類緩存
Map<List<String>, Object> cache;
// 將一個(gè) ClassLoader 映射到該 ClassLoader 的代理類緩存
// private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache = new WeakHashMap<>();
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
}
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
return proxyClass;
} else if (value == pendingGenerationMarker) {
// 正在創(chuàng)建代理類湘今,等待敢朱,代理類創(chuàng)建完成后會(huì)執(zhí)行 notifyAll() 進(jìn)行通知
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
// 代理類為空,往代理類緩存中添加一個(gè) pendingGenerationMarker 標(biāo)識(shí)摩瞎,表示正在創(chuàng)建代理類
cache.put(key, pendingGenerationMarker);
break;
}
} while (true); //這是一個(gè)死循環(huán)拴签,直到代理類不為空時(shí),返回代理類
}
// 以下為生成代理類邏輯
try {
String proxyPkg = null;
// 遍歷接口的訪問修飾符旗们,如果是非 public 的蚓哩,代理類包名為接口的包名
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].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");
}
}
}
if (proxyPkg == null) {
// 如果接口都是 public 的,則用 com.sun.proxy 作為包名上渴,這個(gè)從 $Proxy0 類中可以看到
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
{
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 根據(jù)代理類全路徑和接口創(chuàng)建代理類的字節(jié)碼
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
// 根據(jù)代理類的字節(jié)碼生成代理類
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// 創(chuàng)建的所有代理類集合
// private static Map<Class<?>, Void> proxyClasses = Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
proxyClasses.put(proxyClass, null);
} finally {
synchronized (cache) {
if (proxyClass != null) {
// 創(chuàng)建好代理類后存到代理類緩存中
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
// 否則岸梨,清除之前存入的 pendingGenerationMarker 標(biāo)識(shí)
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
}
將上面的代碼拆分,先來看下動(dòng)態(tài)代理從緩存獲取代理class對(duì)象邏輯:
不同的classloader生成的class不是同一個(gè)對(duì)象稠氮,所以需要根據(jù)classloader和接口類型來唯一標(biāo)志一個(gè)代理class對(duì)象曹阔,接口類型如何獲取在JDK1.7和JDK1.8邏輯上有區(qū)分,這也是JDK1.8效率更高的原因隔披。先看下JDK1.7的接口獲取邏輯
Class<?> proxyClass = null;
// 接口名稱數(shù)組赃份,用于收集接口的名稱作為代理類緩存的 key
String[] interfaceNames = new String[interfaces.length];
// 接口集合,用于檢查是否重復(fù)的接口
Set<Class<?>> interfaceSet = new HashSet<>();
// 遍歷目標(biāo)對(duì)象實(shí)現(xiàn)的接口
for (int i = 0; i < interfaces.length; i++) {
// 獲取接口名稱
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
// 通過反射加載目標(biāo)類實(shí)現(xiàn)的接口到內(nèi)存中
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
...
// 如果接口重復(fù)奢米,拋出異常
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
// 將接口名稱數(shù)組轉(zhuǎn)換為接口名稱列表
List<String> key = Arrays.asList(interfaceNames);
interfaces即外部傳入的需要實(shí)現(xiàn)的接口數(shù)組抓韩,然后會(huì)經(jīng)過一系列邏輯去重處理纠永,最終通過
List<String> key = Arrays.asList(interfaceNames);
來標(biāo)記需要實(shí)現(xiàn)動(dòng)態(tài)代理的class對(duì)象最終要實(shí)現(xiàn)哪些接口。
查看上述代碼可以發(fā)現(xiàn)在去重邏輯內(nèi)部使用到了反射生成class對(duì)象园蝠,該邏輯是每次調(diào)用newProxyInstance生成動(dòng)態(tài)代理對(duì)象時(shí)都會(huì)執(zhí)行的邏輯渺蒿,對(duì)性能是有一定影響的。
拿到唯一標(biāo)記key后如何從緩存拿到class代理對(duì)象邏輯如下:
// 通過 Classloader 獲取或者創(chuàng)建一個(gè)代理類緩存
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
}
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
return proxyClass;
} else if (value == pendingGenerationMarker) {
// 正在創(chuàng)建代理類彪薛,等待茂装,代理類創(chuàng)建完成后會(huì)執(zhí)行 notifyAll() 進(jìn)行通知
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
// 代理類為空,往代理類緩存中添加一個(gè) pendingGenerationMarker 標(biāo)識(shí)善延,表示正在創(chuàng)建代理類
cache.put(key, pendingGenerationMarker);
break;
}
} while (true); //這是一個(gè)死循環(huán)少态,直到代理類不為空時(shí),返回代理類
}
loaderToCache定義如下:
private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache = new WeakHashMap<>();
key是一個(gè)classloader對(duì)象易遣,value是該classloader生成的所有的動(dòng)態(tài)代理對(duì)象彼妻,但是不同的動(dòng)態(tài)代理對(duì)象可能實(shí)現(xiàn)了不同的接口,所以通過一個(gè)Map<List<String>, Object>來保存豆茫,List<String>用來唯一標(biāo)記接口類型侨歉,Object就是真正的代理class對(duì)象了。理解上述意思后再看上面的代碼就很清晰了揩魂。
最終class對(duì)象如何生成就是根據(jù)字節(jié)碼規(guī)則生成一個(gè)文件幽邓,然后往文件中寫入字段,方法等來實(shí)現(xiàn)火脉,實(shí)際上網(wǎng)上有一個(gè)著名的開源框架ASM就是專門用來處理字節(jié)碼插樁工作的牵舵,可以允許開發(fā)者在對(duì)字節(jié)碼并沒有非常熟悉的情況下也可以實(shí)現(xiàn)字節(jié)碼的插樁工作。但是JDK在實(shí)現(xiàn)字節(jié)碼插樁時(shí)并沒有借助該開源框架而是手寫插樁邏輯倦挂。到此JDK1.7的動(dòng)態(tài)代理分析就完成了畸颅。
3 JDK1.8動(dòng)態(tài)代理實(shí)現(xiàn)思路
總體和JDK1.7大致一致,但是在緩存處理上和1.7有部分出入方援。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
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
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache獲取代理class也是通過classloader和接口來唯一標(biāo)志没炒。KeyFactory就是接口key的生成邏輯,
ProxyClassFactory就是生成class的工廠類犯戏,內(nèi)部邏輯和1.7大致一致窥浪。主要是KeyFactory如何生成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);
}
}
}
生成邏輯很簡(jiǎn)單就是通過interfaces長(zhǎng)度來生成不同的key。而在JDK1.7中是通過反射笛丙,然后去重等一系列操作來完成的漾脂,所以在性能上1.8的處理更優(yōu)。
proxyClassCache的get方法部分代碼:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
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
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
......
}
這里要在緩存中獲取到代理class對(duì)象胚鸯,需要classloader和接口來唯一標(biāo)志骨稿。JDK1.8中將classloader做為key,而把接口類型做為subkey,通過這兩個(gè)key來獲取class對(duì)象坦冠。知道這層關(guān)系后再看上面代碼就非常清楚了形耗,后面生成class對(duì)象的代碼省略,和JDK1.7大同小異辙浑。到此1.8的分析也就結(jié)束了激涤。
4 android動(dòng)態(tài)代理實(shí)現(xiàn)
android中動(dòng)態(tài)代理和JDK實(shí)現(xiàn)總體一致,但是真正在生成字節(jié)碼對(duì)象時(shí)的邏輯不是在java層處理判呕,generateProxy是一個(gè)jni方法
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
5 JAVA動(dòng)態(tài)代理為什么只能代理有接口的類倦踢,而不能代理普通類
如果把JDK生成的代理類保存下來,可以看到類似如下結(jié)構(gòu)
public class $proxy0 extends proxy implements 接口類型{
......
}
可以看到代理類已經(jīng)繼承了proxy類侠草,由于java是單繼承結(jié)構(gòu)辱挥,所以代理類對(duì)象不能代理普通的類
為什么需要繼承proxy主要原因:
1 newProxyInstance傳入了一個(gè)invocationHandler對(duì)象處理代理方法,如果生成的代理類不繼承proxy對(duì)象边涕,那么這個(gè)invocationHandler的調(diào)用時(shí)機(jī)晤碘,保存也需要通過字節(jié)碼寫入到代理class中,增加了邏輯復(fù)雜性功蜓。繼承proxy后通用邏輯就可以放在proxy處理
2 在業(yè)務(wù)處理層面上园爷,一般會(huì)在接口層抽象一些公共處理能力,然后通過具體類去實(shí)現(xiàn)對(duì)應(yīng)接口是符合業(yè)務(wù)設(shè)計(jì)思想式撼,所以通過動(dòng)態(tài)代理相應(yīng)的接口童社,對(duì)相關(guān)的處理方法進(jìn)行攔截處理從設(shè)計(jì)角度上看是符合邏輯的。如果在業(yè)務(wù)層面上一定要代理普通類那么需要使用cglib庫來實(shí)現(xiàn)端衰。cglib底層也是通過字節(jié)碼插樁框架ASM來實(shí)現(xiàn)的,通過實(shí)現(xiàn)一個(gè)子類來重寫父類的非final方法達(dá)到動(dòng)態(tài)代理的目的