newProxyInstance
該類的主題結構如下:(為了方便閱讀,我刪除了一些格式驗證之類的程式碼)
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//克隆
final Class<?>[] intfs = interfaces.clone();
//獲取系統安全介面
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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);
}
}
複製程式碼
逐行理解:
- 首先是方法註釋:
返回指定介面的代理類的例項,該介面將方法呼叫分派給指定的呼叫處理程式。
- 由上可知該方法的主要功能是建立一個指定介面的代理類示例,並且將方法的呼叫分派給指定的呼叫處理程式:InvocationHandler,其他引數分別是:類載入器、需要建立代理物件的介面的Class物件。
- clone()介面的Class類並獲取系統的安全介面,安全介面主要用來檢查Class物件的許可權(我是這樣理解的,有錯請指出)
- 呼叫getProxyClass0方法生成代理類, Class<?> cl就是代理類的Class物件
- 呼叫生成代理Class的getConstructor方法獲取其建構函式,並將呼叫處理程式的Class類作為引數傳入
- 檢查該代理Class的Java修飾符,即是否是public還是protected
- 如果該類不是public修飾,則將該物件強制設定為執行時禁止java訪問控制檢查,這樣就可以通過反射呼叫私有的引數和方法
- 通過構造方法的建構函式傳入呼叫處理程式建立目標物件的例項。各個引數自動解包以匹配原始形式引數,並且原始引數和參考引數都根據需要進行方法呼叫轉換。
用一個簡單的流程圖表示這個方法的流程吧:
然後繼續探究該方法會呼叫的幾個主要方法:
getProxyClass0
該方法的方法註釋就簡單的一句話:
生成代理類。 在呼叫此方法之前,必須呼叫checkProxyAccess方法執行許可權檢查。
該方法主體如下:
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);
}
複製程式碼
程式碼的註釋簡單的翻譯如下:
如果存在實現給定介面的類載入器定義的代理類,則只返回快取副本; 否則,它將通過ProxyClassFactory建立代理類
同時這裡還會檢查目標介面的大小,如果超過65535則丟擲異常,這是由JVM限制的,因為Java使用的是UNICODE標準字符集16位,最大為65535.
接著看proxyClassCache引數
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
複製程式碼
這個類具體我也沒有使用過,通過查詢類註釋得知該類的功能:
- 這是一個快取對映對,對映關係:{(金鑰,子金鑰) -> 值}。
- 鍵和值是弱引用,會在GC的時候被清理,但是子鍵是強引用
- 使用建構函式所賦值的subKeyFactory函式從鍵和引數計運算元鍵。
- 使用建構函式所賦值的valueFactory函式從鍵和引數計算值。
因此首次呼叫應該會呼叫new ProxyClassFactory()這個物件,的apply方法
ProxyClassFactory
這個類可以看到它所實現的介面:BiFunction<ClassLoader, Class<?>[], Class<?>>,這是一個函式介面。 它的實現如下:
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
複製程式碼
看起來很長很複雜,其實大部分都是些引數驗證之類的功能,這裡我就按塊來過一遍:
類載入器驗證
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
複製程式碼
主要驗證
- 類載入器是否可以載入目標介面的Class
- 類載入器載入的是否是介面類
- 檢查目標介面Class是否重複
包位置驗證
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
複製程式碼
這段主要驗證的是:
- 介面是否在同一個包下的非public介面。如果是同一個包下的非pubic介面,則將目標修飾符從public final修改為final。並將包名賦值為該介面所在的包名。如果是同一個包下的public介面,則使用預設的com.sun.proxy路徑
設定生成代理類的名稱
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
複製程式碼
將前面的包名和預設的$Proxy字首組合成該Class的名稱,這有點眼熟: com.sun.proxy$Proxy0,這有點類似於記憶體地址的toString()列印。
代理類生成
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
複製程式碼
最後是呼叫ProxyGenerator.generateProxyClass方法並傳入代理類名稱,目標介面物件以及目標介面的修飾符來建立目標介面代理類的byte[],然後呼叫defineClass0方法開闢記憶體空間在記憶體中建立類。