我還是事先聲明我目前寫的都是我的一些理解,僅僅作爲我的筆記作爲參考!
今天的主要內容就是解析動態代理中的newProxyInstance 和 invoke方法
第一部分:newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h):這個方法主要就是獲取代理對象,其實就是委託類的實例,再進一步就是委託類擁有真實業務類的引用,從而這個代理類可以代替真實業務類處理業務邏輯;
ClassLoader:類加載器的作用是什麼呢?顧名思義,就是加載類文件,就是.class字節碼文件(主要由8位字節碼組成),這個部分可以詳細的去學習JVM虛擬機;我的通俗理解就是使用這個類加載器對象可以去加載類對象;
Class<?>[]:類,這個類是有一個private的私有構造方法,它是由JVM進行實例化的,大概所有的類都可以作爲它的實例,比如User.class,可以是它的一個實例,這個Class的組成呢? 我們通過一些元或者初始化的東西來描述一個類:
包名、類名或者接口、修飾符(類的修飾符、接口的修飾符、屬性的修飾符、方法的修飾符)、
方法以及方法參數、成員屬性、
註解等等;
InvocationHandler:顧名思義: 調用處理器接口,只有一個invoke的方法;
第二部分:invoke(target,args)方法:
target: 方法名
args: 傳入方法名中的參數
綜合上面的因素:我們推斷: jvm的類加載器加載某一個類(真實業務類),因爲加載了這個類所以我們就可以獲得這個類的所有的信息(方法、屬性啊),然後通過指定方法(這個類中的特定業務邏輯方法)以及參數就可以進行調用了;
其實還是沒有理解到代理對象如何執行到業務方法的?然後我想理解的是invoke這個方法的底層:
①
- Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
首先我們看下這個方法的實現:在獲得代理實例方法裏面有下面這個方法:
Class cl = getProxyClass(loader, interfaces);//這個方法主要就是獲得代理的真實的類的對象,這個真實的類實現了至少一個接口,這個接口主要是我們自定義的(業務邏輯抽象接口),
- / 加載目標類實現的接口到內存中
- interfaceClass = Class.forName(interfaceName, false, loader);
加載相應的接口到內存中,其實也就是加載.class文件中的Class對象到內存中,
- // 把目標類實現的接口名稱作爲緩存(Map)中的key
- Object key = Arrays.asList(interfaceNames);
- Map cache;
- synchronized (loaderToCache) {
- // 從緩存中獲取cache
- cache = (Map) loaderToCache.get(loader);
- if (cache == null) {
- // 如果獲取不到,則新建地個HashMap實例
- cache = new HashMap();
- // 把HashMap實例和當前加載器放到緩存中
- loaderToCache.put(loader, cache);
- }
最關鍵的是這個一步:
- try {
- // 中間省略了一些代碼 .......
- // 這裏就是動態生成代理對象的最關鍵的地方
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces);
- try {
- // 根據代理類的字節碼生成代理類的實例
- proxyClass = defineClass0(loader, proxyName,
- proxyClassFile, 0, proxyClassFile.length);
- } catch (ClassFormatError e) {
- throw new IllegalArgumentException(e.toString());
- }
- }
- // add to set of all generated proxy classes, for isProxyClass
- proxyClasses.put(proxyClass, null);
- }
我們是通過代理生成器來生成代理類的字節碼,(注意代理類是含有真實業務的類的引用的),
又當我們獲取到一個類的字節碼的時候,有一個方法可以直接獲取這個類的實例:
defineClass0(loader,proxyName,proxyClassFile,0,proxyClassFile.length);