目錄
一.java代理模式的實現
二.手寫模擬JDK動態代理
三.JDK動態代理源碼剖析
正題
一、java代理模式的實現
1.java中代理模式的實現方式
java代理分靜態代理和動態代理,java靜態代理實現有兩種:繼承,聚合。java動態代理實現有兩種:JDK動態代理,CGLIB代理。
2.靜態代理和動態代理的主要區別和特點
區別:
使用靜態代理時,在編譯時期就有確定的.class文件,即java類文件是我們自己創建並編寫的,然後通過編譯器編譯,在項目中生成對應的.class文件;
使用動態代理時,在編譯時期沒有明確的.class文件,即我們並沒有創建java代理類的類文件,因此編譯後也就不會出現.class文件,他是在運行時在內存中動態的產生代理類的類字節碼,並通過類加載器將字節碼載入到jvm中生成對應的類對象。
特點:
靜態代理由於有明確的類文件,所以會產生很多代理類的類文件,在複雜的系統中,會造成類爆炸,不好維護,靈活性低。
動態代理減少了類的編寫與創建,簡化了代理過程,靈活性好,耦合度低。
二、手寫模擬JDK動態代理
1.手寫模擬動態代理的具體過程
大概過程: .java->.class->Class對象->object
具體過程:在程序運行時,利用反射+字符串拼接生成.java類的字符串形式,將此字符串保存到磁盤,經過第三方編譯工具的動態編譯,在磁盤上生成對應的.class文件,然後通過URL類加載器將.class字節碼文件載入到jvm內存中,隨之加載生成類對象,通過類對象獲取構造器對象,通過構造器對象構造生成實例對象,這裏爲什麼不用類對象直接newInstance()的原因是,這個方法調用的是默認的空參構造器,我們生成的代理類可能不存在空參構造,所以爲了通用性,我們採用構造器對象調用指定的構造器方法生成實例對象。
代理邏輯的具體實現類CustomInvocationHandler的作用:該類中的invoke()方法是具體的代理處理邏輯,我們之前拼接的字符串中,在代理類中持有一個CustomInvocationHandler類型的引用,我們每回調用代理類的一個方法實際都是調用的CustomInvocationHandler中的invoke()方法來處理代理邏輯的,我們一會放上我們拼接後的java類看一看便知
1.生成代理對象的工具類
package com.wang.proxy.custom;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ProxyUtil {
//下面這兩個字符串都可以自己定義,使用PATHNAME時得先創建對應的目錄,
//比如我下面寫的這個路徑,需要自己先去D盤下創建com目錄,
//com目錄下在創建一個子目錄wang,你也可以在拼接字符串的時候自動去處理生成目錄,
//使用file.mkdir()創建目錄,CLASSNAME應該和PATHNAME的路徑名稱一致;
public static final String PATHNAME="d:\\com\\wang\\$Proxy.java";
public static final String CLASSNAME="com.wang.$Proxy";
/**
*
* @param interfaces 接口的類對象
* @param h 邏輯處理類
* @return 返回一個代理對象
*/
public static Object newInstance(Class interfaces, CustomInvocationHandler h){
Object proxy=null;
//1.生成java類的字符串形式
String content=generateJavaFile(interfaces);
//2.寫入到磁盤中
File file =new File(PATHNAME);
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
//3.動態編譯磁盤下的.java文件,生成對應的.class
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//4.通過URLClassLoad類加載器,將文件系統中的字節碼文件載入jvm內存,
//並生成對應的類對象。
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass(CLASSNAME);
//5.通過類對象構造實例對象
Constructor constructor = clazz.getConstructor(CustomInvocation.class);
proxy = constructor.newInstance(h);
//clazz.newInstance(); //此處不能用此方法直接構造實例對象的原因是,
//此方法調用類的空參構造器,類中可能沒有空參構造器
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
/**
* 此方法通過字符串拼接生成.java類的字符串形式
* @param interfaces 接口的類對象
* @return .java文件的字符串形式
*/
private static String generateJavaFile(Class interfaces){
Method methods[] =interfaces.getDeclaredMethods(); //獲取到接口下面聲明的所有方法對象
String line="\n";
String tab ="\t";
String infName = interfaces.getSimpleName(); //接口名
String content ="";
String packageContent = "package com.wang;"+line; //包名
String importContent = "import "+interfaces.getName()+";"+line //引包語句
+"import com.wang.proxy.custom.CustomInvocation;"+line
+"import java.lang.Exception;"+line
+"import java.lang.reflect.Method;"+line;
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line; //聲明類的哪一行
String filedContent =tab+"private CustomInvocation h;"+line; //類中的引用,固定的是CustomInvocation h類型,
// 代理類中所有方法的實現直接調用h.invoke()方法就能實現邏輯的動態代理
String constructorContent =tab+"public $Proxy (CustomInvocation h){" +line //構造器語句
+tab+tab+"this.h =h;"
+line+tab+"}"+line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName(); //方法的返回類型
String methodName =method.getName(); //方法名稱
// Sting.class String.class
Class args[] = method.getParameterTypes(); //參數的類型列表,例如[String.class,Integer.class]
String argsContent = ""; //定義方法內的參數內容
String paramsContent=","; //反射獲取方法對象時的getDeclaredMethod()方法內的形參
int flag =0;
String methodArgs="new Object[]{"; //反射獲取方法對象時的getDeclaredMethod()方法內的形參類型
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String p0,Sting p1,
argsContent+=temp+" var"+flag+",";
paramsContent+=temp+".class,";
methodArgs+="var"+flag+",";
flag++;
}
methodArgs=methodArgs.substring(0,methodArgs.length()-1);
if(!paramsContent.equals(",")) {
paramsContent = paramsContent.substring(0, paramsContent.length() - 1);
}else{
paramsContent="";
}
methodArgs+="}";
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(","));
}
if(methodArgs.equals("new Object[]}")) {
methodArgs = "(Object[])null";
}
//"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
String returnContext="";
//"return ("+returnTypeName+")h.invoke(this,method,"+methodArgs+");"+line
if(returnTypeName.equals("void"))
returnContext="h.invoke(this,method,"+methodArgs+");"+line;
else
returnContext="return ("+returnTypeName+")h.invoke(this,method,"+methodArgs+");"+line;
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+interfaces.getName()+"\").getDeclaredMethod(\""+methodName+"\""+paramsContent+");"+line
+tab+tab+returnContext;
methodContent+=tab+"}"+line;
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
return content;
}
}
在此,我們先看一下generateJavaFile()方法生成的字符串,寫到磁盤上的效果如下:
這裏只是簡單的代理的UserService接口的一些方法,實際JDK動態代理還代理的Object下的所有方法,我們將在源碼剖析時展示JDK生成的類文件樣式;
package com.wang;
import com.wang.proxy.custom.service.UserService;
import com.wang.proxy.custom.CustomInvocation;
import java.lang.Exception;
import java.lang.reflect.Method;
public class $Proxy implements UserService{
private CustomInvocation h;
public $Proxy (CustomInvocation h){
this.h =h;
}
public String update(String var0)throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("update",String.class);
return (String)h.invoke(this,method,new Object[]{var0});
}
public void save()throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("save");
h.invoke(this,method,(Object[])null);
}
public void save(String var0)throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("save",String.class);
h.invoke(this,method,new Object[]{var0});
}
}
2.統一代理邏輯的接口CustomInvocation
package com.wang.proxy.custom;
import java.lang.reflect.Method;
public interface CustomInvocation {
Object invoke(Object proxy, Method method,Object[] args);
}
3.代理邏輯的具體實現類CustomInvocationHandler
package com.wang.proxy.custom;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CustomInvocationHandler implements CustomInvocation {
private Object target;
public CustomInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object result=null;
try {
System.out.println("代理對象的前置增強方法!");
result= method.invoke(target,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return result;
}
}
4.測試類
package com.wang.proxy.custom;
import com.wang.proxy.custom.service.UserServerImpt;
import com.wang.proxy.custom.service.UserService;
public class Client {
public static void main(String[] args) {
UserService o = (UserService) ProxyUtil.newInstance(UserService.class,new CustomInvocationHandler(new UserServerImpt()));
try {
o.save();
System.out.println(o.update("帶參數和返回值的目標方法"));
o.save("帶參數的目標方法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.運行結果
2.手寫模擬動態代理的一些區別以及缺點
在此先說明,我們只是在這手寫模擬了JDK動態代理的過程,實際過程比我們模擬的要複雜的多,JDK動態代理沒有生成.java文件,是直接將數據寫入到byte[]數組中,然後直接加載這個數組生成對應的類對象,稍後我們就會看到具體實現,現在我們來說手寫模擬的缺點如下:
缺點:是我們自定義的動態代理,生成.java文件需要磁盤IO,動態編譯也需要磁盤IO,相對於JDK動態代理來說性能差很多。
區別:用的類加載器不同,我們自己模擬的動態代理,採用的URLClassLoad,因爲.class文件是在磁盤上的,所以我們需要通過URL去獲取。但是JDK自帶的動態代理是採用當前類的類加載器,因爲此時的.class是在內存中的,以byte[]數組的形式存在的,所以可以直接用當前類的類加載器加載生成對應的類對象。
三.JDK動態代理源碼剖析
1.JDK動態代理的基本使用
在弄清使用以及原理前,我們先要統一一些概念:
目標對象:要被代理的對象;
代理對象:用JDK動態代理生成的對象
目標類的方法對象:就是目標類中的每個方法自己特有的對象,在java中萬物皆對象,一個方法本身也是一個對象,這個對象具有invoke()方法,可以調用方法;
如題,我們要使用動態代理,僅僅需要寫一個邏輯處理類,其他的類JDK已經幫我們寫好了, 此處理類需要一個目標對象的引用,因爲在該類的invoke()方法內調用method.invoke()方法時(這兩個invoke方法是不同的,往下看邏輯處理類便知),需要目標對象才能調用目標對象的方法,比如method是目標類中的某個方法對象,method.invoke(obj,args)這個obj應該是目標對象纔可以實現方法的調用,所以我們在編寫邏輯處理類時需要傳入目標對象。
被代理類對應的接口:
package com.wang.Design.proxy.a;
public interface Service {
public void save();
}
被代理類:
package com.wang.Design.proxy.a;
public class RealService implements Service{
public void save(){
System.out.println("正在保存");
}
}
測試類:
package com.wang.Design.proxy.a;
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//目標對象
RealService service=new RealService();
System.out.println("目標對象所在的類"+service.getClass().toString());
Service proxyService = (Service) Proxy.newProxyInstance(RealService.class.getClassLoader(), RealService.class.getInterfaces(),(new <RealService>Handler(service)));
System.out.println("代理對象所在的類"+proxyService.getClass().toString());
//執行代理對象代理的方法
proxyService.save();
}
}
這裏涉及到一個主要的生產代理實例的靜態方法Proxy.newInstance()
這個方法返回一個代理對象,該對象所在的代理類用loader類加載器載入jvm,這個代理類實現了interfaces[]這些接口,因此會實現這些接口中的所有方法,每個方法的具體實現邏輯是h實例,也就是我們下面要寫的邏輯處理類的實例;
下面的是邏輯處理類,相當於我們模擬動態代理類時的CustomInvocationHandler類
package com.wang.Design.proxy.a;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Handler<T> implements InvocationHandler {
private T service;
public Handler(){}
public Handler(T service) {
this.service=service;
}
public Handler(Class<T> clazz) throws IllegalAccessException, InstantiationException {
this.service=clazz.newInstance();
}
@Override
//proxy表示代理類的實例,method表示傳入的方法對象,實際運行時這個method應爲目標類的方法對象
//args代表參數數組,實際運行時這個args應該爲目標類的方法中的參數類型數組,例如[String.class,Intger.clsss]
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result; //用於接收目標方法的返回結果,可能爲Null,說明目標方法沒返回值
if(method.getName().equals("save")){
System.out.println("開啓事務");
result=method.invoke(service,args); //這句話代表調用了目標對象的目標方法,
//在這句話的前後即爲我們可以添加的處理邏輯
System.out.println("關閉事務");
}
return result;
}
}
運行結果:
2.JDK動態代理的代理流程
在上面,我們寫了一個邏輯實現類Handler,我們通過Proxy.newInstance方法返回了一個代理實例,實質上這個實例所在的代理類含有一個InvocationHandler類型的引用h,當我們調用代理類的某個方法時,這個方法的具體實現就是調用h的invoke()方法,我們將方法對象(method)傳入,以便invoke()方法內部可以自己調用目標類中的目標方法,在invoke()的實現中,我們通過在method.invoke()方法執行的前後加入打印語句,來模擬要增強的動作,注意method.invoke()和InvocationHandler接口內的invoke()方法不是一個東西,下面我給上JDK動態代理生成代理類的.class字節碼,大家便能更加理解。
import com.wang.dao.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class wang extends Proxy implements UserDao {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m0;
private static Method m3;
public wang(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 void save() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 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);
}
}
public final void update() throws {
try {
super.h.invoke(this, m3, (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"));
m4 = Class.forName("com.wang.dao.UserDao").getMethod("save");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.wang.dao.UserDao").getMethod("update");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我們發現這個代理類每個方法的具體實現都是調用Proxy類中的h的invoke()方法,這個h即爲我們寫的邏輯實現類。還可以看到每個方法對象都是通過反射獲得的目標類中的具體方法的方法對象。
3.JDK動態代理流程的源碼分析
通過上面分析得知,我們的代理類實例所在的代理類,是在程序運行期間動態生成的,而不是預先編譯而成的,而每個代理類都繼承了Proxy類,此類中含有一個InvocationHandler類型的引用,這也說明了JDK動態代理不能基於繼承實現,因爲java是單繼承的,哪這個代理類實例到底是怎麼生成的呢?
我們生成的代理類實例,是通過Proxy中的靜態方法 newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 生成的,我們從這開始,逐步分析生成實例的過程:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
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);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {//還是在檢查權限
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//這裏通過類對象獲取到了構造器對象,通過構造器對象構造出的對象實例,
//構造時調用的帶參構造器將邏輯處理對象h注入,此處沒有直接使用clazz.newInstance()原因之前說過。
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;
}
});
}//通過帶參構造傳入h,使代理對象擁有h對象;
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(loader, intfs)方法源碼:
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}
這個方法十分簡單,第一行是一個參數驗證,重要的是return語句前面的源碼註釋,翻譯過來如下:
如果實現給定接口的給定加載器定義的代理類存在,則只返回緩存的副本;否則,它將通過ProxyClassFactory創建代理類
意思很明顯,如果當前要返回的代理對象所在的代理類存在,則直接從緩存中取出返回,不存在則用工廠生產一個代理類。我們可以瞭解到JDK底層用了緩存來提高獲取代理類對象的性能
proxyClassCache.get(loader, interfaces)方法源碼:
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);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
這個方法前面就是在找緩存,判斷緩存是否有,如果沒有,會進入到下面while (true) 的死循環,直到return value; 我們從前一個方法getProxyClass0(loader, intfs)瞭解到,當前這個方法返回的是一個類對象,所以可以斷定這個value就是類對象,我們觀察value在哪被賦過值,可以看到 V value = supplier.get(); ,value是從supplier.get()方法獲取的,我們進入到supplier.get()源碼:
這個get()源碼要找,我們點入到get方法看到他是一個接口定義的方法,實現有很多,和JDK動態代理相關的類是WeakCache,所以我們找到了WeakCache下的get方法
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// try replacing us with CacheValue (this should always succeed)
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
我們只是簡單的尋找value怎麼產生的,內部的一些額外操作,例如取緩存,判斷權限之類的就略過了,直接看value是怎麼產生的。我們看到此方法還是返回的一個value,觀察那個地方修改過value,發現value = Objects.requireNonNull(valueFactory.apply(key, parameter));
可以看到是valueFactory.apply(key, parameter);方法生成了一個value,由名字可以得出,應該是工廠生產出了這個類對象,我們進入valueFactory.apply(key, parameter);方法,發現他也是一個接口,具體實現類有如下,我們調出和Proxy相關的,發現ProxyClassFactory類
進入ProxyClassFactory類的apply方法:
@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());
}
}
}
直接上重點,我們找到return的地方,發現return的是一個 defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);方法,很顯然這個方法返回了一個代理類類對象,這個方法的參數前兩個都是提供好的(loader表示類加載器,proxyName表示代理類的名字),我們看第三個參數proxyClassFile,可以看到他是一個byte[]數組,可以瞭解到這個方法是用類加載器+類名+一個比特數組生成的,這個比特數組在哪生成的呢?我們看到return語句的上一行: byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
我們可以瞭解到這個proxyClassFile 其實就是代理類的字節碼文件,當前是以byte[]的形式存在於內存中的,他通過代理類名+接口+標記生成了這個byte[],這個數組實際上是外部的.class文件載入內存後的存在形式,我們可以看到,這裏是直接生成的byte[],我們進入此方法,觀察byte[]數組是怎麼形成的
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
可以看到這是OpenJDK的源碼,我們看到最後返回的var4,觀察var4怎麼生成的;
final byte[] var4 = var3.generateClassFile(); 進入此方法
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
發現這個byte數組是由 var13.toByteArray();返回的,這個var13在方法中
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
可以看到 在源碼中生成了一個比特流,然後通過向流中寫入類的字節碼信息,最後以byte[]形式返回,看下圖,可能會有疑問他寫的var14,最後怎麼返回var13?其實他們是一個,var14.writeShort方法點進入會發現,他持有一個var13,這個var13是通過var14的構造方法傳入的,實質還是在var13中寫入。
這個out實質上就是構造器傳入的var13;
這裏可以看到,JDK動態代理底層是直接生成字節碼在內存中的byte流的,而不是先寫.java類文件再編譯再載入的,我們回到剛纔 return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);這個方法,我們現在知道了proxyClassFile的出處,我們看看defineClass0是怎麼用byte[]生成這個類對象的
我們發現這個方法是一個native方法,方法的具體實現可能是由C或C++實現的。omg到底了,再往下走就觸碰到我的知識盲區了…
我們知道了用字節碼文件的byte數組和類加載器,通過這個方法可以生成對應的類對象。
總結一下動態代理的流程:實質上先通過ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);方法,用反射+接口,將生成的內容追加到流中,生成對應的byte[],這個數組內容實際上就是字節碼(我們可以將這個byte[]通過輸出流輸出到文件系統中,通過IDEA反編譯即可看到字節碼文件)。
然後通過本地方法
defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
傳入字節碼和類加載器,生成對應的代理類類對象並返回,中間涉及到很多的權限認證、緩存架構、異常判斷等操作,但本質的實現就是這樣。