寫在前面
本篇博客是基於對動態代理,java的重寫,多態特性了解的基礎上對於源碼的閱讀,先提出幾個問題
1.從靜態代理變成動態代理需要解決兩個問題,如何動態實現被代理類的接口並摘取接口中的方法,如果動態的為被代理方法添加附加邏輯
2.給出一個例子:
interface a {
String get() throws Exception;
}
interface b {
Object get() throws NullPointerException;
}
}
如果我要代理即實現了a又實現了b的類的get方法,那么在代理類$Proxy0中基於a和b的get方法動態生成的get方法的描述是什么?怎么實現?
3.能否舉出代理失敗的例子?(兩種?)
閱讀建議:涉及的方法以及域比較多,前后是連貫的,抓住關聯
重要的域
先從proxy類開始
//一個靜態常量,后面會用到
private static final Class<?>[] constructorParams = { InvocationHandler.class };
//代理類的緩存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
//InvocationHandler類型變量,注意它的訪問權限為protected,也就是說子類可以為它賦值
protected InvocationHandler h;
newProxyInstance
/**
*loader:常常將被代理類的classloader傳入
*interfaces:被代理類的接口集合
*h:代理輔助類,被代理方法的實際調用者
**/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//檢查h是否為空,如果為空則拋出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);
}
//反射獲得代理類的構造函數,其中傳入參數constructorParams是前面定義的靜態常量,這也間接說明代理類的共同特點:g構造函數傳入參數為InvocationHandler類型
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);
}
}
getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//官方解釋:如果在緩存中找到匹配loader和interfaces的代理類則直接返回,否則通過ProxyClassFactory新建一個
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache在上述域中已經說明,它是屬於WeakCache類型,傳入了KeyFactory和ProxyClassFactory的實例作為參數進行了實例化,下面我們來看下get方法
get
public V get(K key, P parameter) {
//確保interfaces不為空
Objects.requireNonNull(parameter);
//在這個方法中有很多緩存的操作,不懂,省略處理,以后填坑
expungeStaleEntries();
//省略
while (true) {
if (supplier != null) {
//這句和代理類的生成有關
V value = supplier.get();
//省略
}
上述注釋的supplier.get()實現另一個方法的調用,在weakCache中有一個Factory靜態內部類,它實現了supplier接口,因此supplier調用get實際是調用其子類Factory對象的get方法
get
public synchronized V get() {
//緩存操作不懂省略
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
return null;
}
V value = null;
try {
//很明顯可以發現下面的valueFactory.apply是用來生成代理類的
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
}
//省略
}
}
valueFactory是上述在實例化weakCache時傳入的ProxyClassFactory的對象的引用,下面查看它的apply方法
apply
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//用來判斷interfaces中是否重復的set
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
//用我們傳入的classloader加載interface
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
//如果上述加載的interfaceclass和我們直接傳入的intf不同,則說明傳入的loader和interface不匹配,拋出異常
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
//判斷是否為接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
//判斷interfaces是否有重復
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//代理類的package值
String proxyPkg = null;
//代理類的訪問權限
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//遍歷判斷interfaces中是否有非public訪問權限的interface,如果有的話,代理類所在的包必須和它一致
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
//如果不是公有權限
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
//下面三行代碼用來獲得intf所在包,例如若intf = InvacationHandler.class,pkg將等於java.lang.reflect.
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
//如果有多個非public的interface並且它們不在同一個包中,將拋出異常
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//如果所有的interfaces都為public,那么代理類所在的包為com.sun.proxy.
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/**下面兩行代碼用到兩個ProxyClassFactory中的域,分別是 :
/*1.private static final String proxyClassNamePrefix = "$Proxy";
/*2.private static final AtomicLong nextUniqueNumber = new AtomicLong();
**/
long num = nextUniqueNumber.getAndIncrement();
//例如proxyName為com.sun.proxy.$proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//核心邏輯的調用出現了
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//defineClass0為本地方法,用來動態加載class文件
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
經過數次跳轉,終於來到了核心代碼地,即ProxyGenerator的generateProxyClass方法,sun的jdk中該部分代碼不是開源,可以參考openJdk,接下來僅貼出主要代碼
generateProxyClass
public static byte[] generateProxyClass(final String name, Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
final byte[] classFile = gen.generateClassFile();
//中間省略
return classFile;
}
在介紹ProxyGenerator的其他方法之前先來看一下其中的一些域
/**一個用來過渡處理(怎么處理?)所有method對象的hashmap,String的組成是Method對象的name加上參數列表(為什么這樣標識),對於代理類而言每一個String標識對應的List中必須只有一個元素,否則方法就重復了**/
private Map<String, List<ProxyMethod>> proxyMethods = new HashMap<String,List<ProxyMethod>>();
//包含代理類所有method信息的集合
private List<MethodInfo> methods = new ArrayList<>();
//包含代理類所有field信息的集合
private List<FieldInfo> fields = new ArrayList<>();
generateClassFile
private byte[] generateClassFile() {
//step1:針對所有接口中的方法生成Method對象
//首先向proxyMethods中添加Object的hashcode,equals,toString三個method對象
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//然后添加接口中的method對象
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]);
}
}
//對每一個String標識的List中的所有ProxyMethod的返回值類型進行兼容性處理(涉及到重寫特性)(為什么要處理?怎么處理)
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//step2:向methods和fields中分別添加methodInfo和FieldInfo
try {
//添加構造函數
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//對於每個method對象添加靜態引用
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
//添加在proxyMethods這個hashMap中走過一遭的的ProxyMethod的methodInfo
methods.add(pm.generateMethod());
}
}
//添加靜態初始化方法,例如:<clinit>,類在被加載的時候,為所有靜態初始化的變量生成一個clinit方法,然后在里面執行賦值
methods.add(generateStaticInitializer());
//step3:漫長的拼class文件系列,不再展示
接下來是解釋前面提出的過渡處理以及開頭提出的問題2
addProxyMethod
//該方法一方面是向proxyMethods這個hashmap中添加不同標識的method對象,另一方面是針對相同的標識進行兼容性處理
private void addProxyMethod(Method m, Class fromClass) {
//反射獲得method的name,parameterTypes,returnType,exceptionTypes
String name = m.getName();
Class[] parameterTypes = m.getParameterTypes();
Class returnType = m.getReturnType();
Class[] exceptionTypes = m.getExceptionTypes();
//形成該m的sig(標識)即name+參數列表
String sig = name + getParameterDescriptors(parameterTypes);
//proxyMethods是上述提到的hashMap(key為sig)
List<ProxyMethod> sigmethods = proxyMethods.get(sig);
//如果在proxyMethods中能找到和m標識一樣的ProxyMethod,例如:開頭問題2中a和b的標識就是一樣的
if (sigmethods != null) {
for (ProxyMethod pm : sigmethods) {
//並且返回類型相同
if (returnType == pm.returnType) {
//兼容性處理:將pm的異常大小要小於等於pm和m中的最小值(重寫特性),不需要向proxyMethods中添加m了
List<Class> legalExceptions = new ArrayList<Class>();
collectCompatibleTypes(
exceptionTypes, pm.exceptionTypes, legalExceptions);
collectCompatibleTypes(
pm.exceptionTypes, exceptionTypes, legalExceptions);
pm.exceptionTypes = new Class[legalExceptions.size()];
pm.exceptionTypes =
legalExceptions.toArray(pm.exceptionTypes);
return;
}
}
}
//如果沒有找到相同的標識,proxyMethods添加一個新的key-value鍵值對,value是一個list初始容量為3
else {
sigmethods = new ArrayList<ProxyMethod>(3);
proxyMethods.put(sig, sigmethods);
}
//無論是在proxyMethods中與m有相同標識但是返回類型不同,還是沒有相同標識,都要向proxyMethods對應key的list中添加m
sigmethods.add(new ProxyMethod(name, parameterTypes, returnType,
exceptionTypes, fromClass));
}
經歷該方法后,proxyMethods中某個key的list的ProxyMethod個數可能大於1(如果直接用來生成代理類,將會有重復的方法),這些ProxyMethod的name和參數列表相同,返回類型不同,接下來checkReturnTypes處理
checkReturnTypes
在前面是通過
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
這段代碼來進入checkReturnTypes方法的
/**checkReturnTypes方法是專門進行兼容性處理,對於需要實現的接口中有重復的方法(如前面問題2的a,b),為了對實現這些接口的類都進行代理,代理類必須生成一個同時對它們進行重寫了的方法,也就是說動態生成的這個方法,在每個接口看來都是對它內部抽象方法的重寫,真相就是:代理類方法的返回類型必須是interfaces中所有重復方法返回類型的中的一個,並且這個返回類型是其他重復方法返回類型的子類(例如:參照開頭interface a,b,代理類的get方法的返回類型是String)**/
static void checkReturnTypes(List<ProxyMethod> methods) {
//如果proxyMethods中該標識的list元素小於2,那就不需要處理了
if (methods.size() < 2) {
return;
}
//用來記錄已經處理的所有返回類型中,已經兼容其它返回類型的返回類型(例如:參照開頭interface a,b,在處理了它們的重復get方法之后,代理類方法返回類型為String)
LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>();
//continue的跳轉標簽
nextNewReturnType:
for (ProxyMethod pm : methods) {
Class<?> newReturnType = pm.returnType;
//如果某個Proxymethod的返回類型是基本類型(int,char,long,double等),就直接拋出異常,因為基本類型和其它返回類型都不兼容
if (newReturnType.isPrimitive()) {
throw new IllegalArgumentException(
"methods with same signature " +
getFriendlyMethodSignature(pm.methodName,
pm.parameterTypes) +
" but incompatible return types: " +
newReturnType.getName() + " and others");
}
boolean added = false;
//遍歷uncoveredReturnTypes
ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
while (liter.hasNext()) {
Class<?> uncoveredReturnType = liter.next();
//如果pm的返回類型是uncoveredReturnType的父類,那么uncoveredReturnType也兼容pm,直接continue
if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
assert !added;
continue nextNewReturnType;
}
//如果pm的返回類型是uncoveredReturnType的子類,那么pm代替uncoveredReturnType兼容其它已經處理的returntype
if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
if (!added) {
liter.set(newReturnType);
added = true;
}
//uncoveredReturnType中可能也有多個returnType,它們之間不兼容,當下面分支執行的時候,就說明pm的返回類型同時兼容uncoveredReturnType中的某些returnType,那么在uncoveredReturnType中這些returnType就可以移除了
else {
liter.remove();
}
}
}
//向uncoveredReturnTypes中添加不兼容的類,
if (!added) {
uncoveredReturnTypes.add(newReturnType);
}
}
//如果uncoveredReturnTypes中返回類型多於1個,也就是說:不存在一個返回類型兼容其它所有的,那么代理類中對於該方法也就無法重寫了,拋出異常
if (uncoveredReturnTypes.size() > 1) {
ProxyMethod pm = methods.get(0);
throw new IllegalArgumentException(
"methods with same signature " +
getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) +
" but incompatible return types: " + uncoveredReturnTypes);
}
}
表述的比較拗口,下圖展示checkReturnTypes方法

最后LinedList就是兼容所有類型的類
$proxy0代碼
public final class $Proxy0 extends Proxy implements a,b{
//根據方法生成的靜態的域
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
//類加載時,會在clinit方法中進行初始化
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("my.java.reflect").getMethod("get",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
//還記得Proxy類中InvocationHandler h的訪問權限為protected嗎?這個時候賦值
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String get() {
try {
return (String)super.h.invoke(this, m3, null);
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
以上
