第15章 反射之動態代理

課程回顧

反射的概念: 運行期創建對象並調用對象的方法
①反射類的信息: 類名稱, 類的父類,類的接口,類的包信息,類的註解; newInstance創建對象
②反射類的屬性: 屬性名稱,屬性的類型,屬性的修飾符,調用屬性的set/get方法, 強制訪問
③反射類的構造方法: Constructor構造方法, 可以獲取無參,有參; newInstance(Object … args)
④反射類的普通方法: Method, 獲取所有聲明的方法,獲取自身和父類的方法,獲取指定的方法, invoke方法
⑤反射類的main方法: getMethod(“main”, String[].class),mm.invoke(null, new Object[]{ new String[]{} })
⑥反射越過泛型檢查:
⑦反射jar包的類信息: 獲取類信息,獲取類的屬性,類的方法
⑧發射創建數組: Array.newInstance(Integer.class, 10)
⑨反射獲取類泛型信息: 獲取方法的泛型信息,獲取返回值泛型信息
⑩手寫Spring框架: 讀取配置文件, 創建對象,放到Map, getBean根據key獲取對象

靜態代理

代理: 代表公司, 區域代理; 李寧鄭州總代理; 代表公司在本區域銷售李寧公司的產品. 媒婆: 代理,
靜態代理一般只代理一個公司的商品, 李寧專賣店: 只銷售李寧公司的產品.
在這裏插入圖片描述

還有一種商業形式: 大型商場, 連鎖超市; 代理的衆多公司的商品, 不再限於一家公司. 簡單理解可以說動態代理.
在這裏插入圖片描述

說說寶寶的故事: 王寶強的故事, 漢語八級考試試題: 寶寶生的寶寶不象寶寶, 嚇死寶寶了.
王寶強: 被代理人
宋喆: 代理人 經紀人
我想找王寶強做個品牌形象代言人, 我先找宋喆, 經紀人負責討價還價; 但是真正的做廣告的是王寶強. 靜態代理

package com.hanker.staticproxy;
//娛樂共同的約束--接口
public interface Enterment {
	static final public   String AAA="XXX";
	public abstract void dance();
	void sing();
}
//==================
package com.hanker.staticproxy;
//真實對象---演員類
public class Actor implements Enterment{

	private String name;//演員名稱
	
	public Actor(String name) {
		super();
		this.name = name;
	}

	public void dance() {
		System.out.println(name+"跳舞......");		
	}
	
	public void sing() {
		System.out.println(name+"唱歌.....");
	}
}
//=================
package com.hanker.staticproxy;
//代理對象---中間人,代理人, 知名社會活動家
public class MiddleMan implements Enterment{
	private  String name;//姓名	
	private Actor actor;//被代理的演員對象
	
	public MiddleMan(String name, Actor actor) {
		super();
		this.name = name;
		this.actor = actor;
	}

	public void dance() {
		System.out.println(name + " 哥們收點好處費.....");
		actor.dance();
		System.out.println(name+ " 下次繼續合作...");
	}
	
	public void sing() {
		System.out.println("--開始唱歌-----");
		actor.sing();
		System.out.println("--結束唱歌-----");
	}
}
//===============
package com.hanker.staticproxy;
//測試類
public class Test {

	public static void main(String[] args) {
		Actor actor = new Actor("寶寶");//創建一個演員
		MiddleMan mm = new MiddleMan("宋喆",actor);
		mm.dance();
		mm.sing();
	}
}

以上是靜態代理, 代理類只能代理一類對象. 如何實現代理任意類型的對象? 動態代理: 在java反射包中提供的有幫助類: Proxy 代理, InvocationHandler 調用處理器.
可以通過一個真實對象, 對應一個代理對象; 這樣的問題是: 代理對象的泛濫.

動態代理

還有一種業務: 影視公司, 會簽約一批藝人, 影視公司不再是一個明星的代理; 而是代理一批明星. 也可以理解爲動態代理.
需求:
在執行方法之前答應日誌信息, 在執行方法之後打印日誌信息;
統計方法執行的時間: 在方法執行之前記錄一個時間, 在方法執行之後再記錄時間; 兩者相減即可.
也可以是執行權限檢查: 開當前用戶有沒有權限執行該方法.核心: 在不改變原始方法的前提下

動態代理Proxy類簡介

public class Proxy extends Object implements Serializable
//屬性
protected InvocationHandler h  //該代理實例的調用處理程序。  
//構造方法
protected  Proxy(InvocationHandler h) 
//從一個子類(通常是一個動態代理類)構造一個新的 Proxy實例,具有指定的調用處理程序的值。  
//普通方法

//返回指定代理實例的調用處理程序。  
static InvocationHandler getInvocationHandler(Object proxy) 
//給出類加載器和接口數組的代理類的 java.lang.Class對象。          
static Class<?> getProxyClass(ClassLoader loader,<?>... interfaces) 
//如果且僅當使用 getProxyClass方法或 newProxyInstance方法將指定的類動態生成爲代理類時,則返回true。 
static boolean isProxyClass(Class<?> cl) 
//返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序。     
static Object newProxyInstance(ClassLoader loader,   //類加載器
                               Class<?>[] interfaces,//接口數組
                               InvocationHandler h)  //調用處理器

動態代理類加載器

ClassLoader : 類加載器 ,把java的字節碼文件加載到jvm執行; 當然之前要做一些列的檢查,連接,校驗.
公共汽車: jvm
ClassLoader: 售票員, 乘務員, 負責把客人拉到車上. 保證加載到jvm中的字節碼是有效的,安全的,沒有被人爲修改過.
在這裏插入圖片描述

Class<?>[] interfaces,//接口數組 被代理對象實現的接口數組
InvocationHandler h : 調用處理器, 纔是真正調用業務方法的地方.

動態代理案例

package com.hanker.dynamicproxy;
public interface ProductManager {
	int addProduct();//添加方法
	int deleteProduct(int no);//刪除方法
}
//===========================
package com.hanker.dynamicproxy;
//要代理的業務對象, 不允許修改源代碼
public class ProductManagerImpl implements ProductManager {
	@Override
	public int addProduct() {
		System.out.println("執行添加商品");
		return 1;
	}
	@Override
	public int deleteProduct(int no) {
		System.out.println("執行刪除商品..."+no);
		return 1001;
	}
}
//=============沒有代理對象==============
package com.hanker.dynamicproxy;

public class Test {

	public static void main(String[] args) {
		test1();
	}
	private static void test1() {
		ProductManger manager =new ProductMangerImpl();
		manager.addProduct();
		int result = manager.deleteProduct(1002);
		System.out.println("返回值:"+result);
	}
}
//=============通過Proxy創建代理對象======================
package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//代理工廠類
public class ProxyFactory {
	//被代理對象
	private Object target;
	//初始化代理對象
	public ProxyFactory(Object target) {
		this.target = target;
	}
	//獲取代理對象
	public Object getProxy() {
		Object  proxy = null;
		//調用處理器
		InvocationHandler h = new  MyLogInvocationHanler(target);
		//僅僅是創建代理對象,方法調用在哪裏?
		proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), //被代理對象的類加載器
				target.getClass().getInterfaces(),  //被代理對象實現的接口
				h);                                 //調用處理器
		//返回代理對象
		return proxy;
	}
	
}
//============調用處理器 =============
package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyLogInvocationHanler implements InvocationHandler {
	//被代理的對象
	private Object object;
	//初始化被代理對象
	public MyLogInvocationHanler(Object object) {
		this.object = object;
	}

	/**
	 * Object proxy 被代理對象 
	 * Method method 被調用的方法
	 * Object[] args 被調用的方法的參數
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//獲取調用方法名稱
		String name = method.getName();
		before(name);//添加日誌
		//調用真實對象的方法
		Object result = method.invoke(object, args);
		after(name);//添加日誌
		return result;
	}
	
	public void before(String methodName) {
		System.out.println("開始執行"+methodName);
	}
	public void after(String methodName) {
		System.out.println("結束執行"+methodName);
	}

}
//===============測試方法==================
package com.hanker.dynamicproxy;

public class Test {

	public static void main(String[] args) {
		test2();
	}
	private static void test2() {
		//創建被代理對象
		ProductManger manager =new ProductMangerImpl();
		//把被代理對象傳入代理工廠
		ProxyFactory factory = new ProxyFactory(manager);
		//獲取代理對象
		ProductManger proxy = (ProductManger) factory.getProxy();
		//查看代理類的名稱
		System.out.println("代理對象的名稱:"+proxy.getClass().getName());
		//調用代理對象的方法
		proxy.addProduct();
		proxy.deleteProduct(10000);
	}
}    

動態代理執行過程

測試類----代理對象------InvocationHandler的實現類-------真實對象.
在這裏插入圖片描述

ProxyFactory只負責創建代理對象,並指定一個調用處理器類: MyLogInvocationHandler
MyLogInvocationHandler再調用真實對象的業務方法, 在調用真實業務對象的方法之前和之後可以添加日誌處理

案例2:

package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyTimeInvocationHandler implements InvocationHandler {

	private Object target;
	public MyTimeInvocationHandler(Object  target) {
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		long start = System.currentTimeMillis();//當前時間的毫秒數
		Object result = method.invoke(target, args);
		long end = System.currentTimeMillis();
		System.out.println("耗時:" + (end-start) + "毫秒");
		return result;
	}

}
//=============================
package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//代理工廠類
public class ProxyFactory {
	//被代理對象
	private Object target;
	//初始化代理對象
	public ProxyFactory(Object target) {
		this.target = target;
	}
	//獲取代理對象
	public Object getProxy() {
		Object  proxy = null;
		//調用處理器
		//InvocationHandler h = new  MyLogInvocationHanler(target);
		InvocationHandler timeHandler = new MyTimeInvocationHandler(target);
		//僅僅是創建代理對象,方法調用在哪裏?
		proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), //被代理對象的類加載器
				target.getClass().getInterfaces(),  //被代理對象實現的接口
				timeHandler);                       //調用處理器
		//返回代理對象
		return proxy;
	}
	
}
//==========測試類沒有修改==============

多級代理: 日誌+時間

//被代理對象
package com.hanker.dynamicproxy;
//要代理的業務對象, 不允許修改源代碼
public class ProductMangerImpl implements ProductManger {
	@Override
	public int addProduct() {
		System.out.println("執行添加商品");
		try {
			Thread.sleep(1000);//模擬業務方法的執行
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return 1;
	}
	@Override
	public int deleteProduct(int no) {
		System.out.println("執行刪除商品..."+no);
		try {
			Thread.sleep(2000);//模擬業務方法的執行
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return 1001;
	}
}
//===========代理工廠類==================
package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//代理工廠類
public class ProxyFactory {
	//被代理對象
	private Object target;
	//初始化代理對象
	public ProxyFactory(Object target) {
		this.target = target;
	}
	//獲取代理對象
	public Object getProxy() {
		Object  proxy = null;
		//調用處理器		
		InvocationHandler timeHandler = new MyTimeInvocationHandler(target);
		//僅僅是創建代理對象,方法調用在哪裏?
		proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), //被代理對象的類加載器
				target.getClass().getInterfaces(),  //被代理對象實現的接口
				timeHandler);                       //調用處理器
		//返回代理對象
		return proxy;
	}	
}
//===================測試類======================
package com.hanker.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
	public static void main(String[] args) {
		//如果我想即輸出日誌又記錄時間怎麼辦? 
		//            鞋廠(200元)--省代(300元)--市代理(400元)--縣級代理(500元)
		//需要兩級代理: 公司--省代---市代理---縣級代理
		//創建被代理對象
		ProductManger manager =new ProductMangerImpl();
		//把被代理對象傳入代理工廠
		ProxyFactory factory = new ProxyFactory(manager);
		//獲取代理對象: proxy省代理
		ProductManger proxy = (ProductManger) factory.getProxy();
		//查看代理類的名稱
		System.out.println("代理對象的名稱:"+proxy.getClass().getName());
		//proxy.addProduct();//一級代理
		//二級代理要增加的內容
		InvocationHandler h = new  MyLogInvocationHanler(proxy);//傳入一級代理對象
		ProductManger proxy2 = (ProductManger)Proxy.newProxyInstance(
				proxy.getClass().getClassLoader(), 
				proxy.getClass().getInterfaces(), h);				
		//調用代理對象的方法
		proxy2.addProduct();
		proxy2.deleteProduct(10000);
	}
}    

執行效果如下圖所示, 需要強調的是MyBatis的插件開發就是多級代理模式實現的.
在這裏插入圖片描述

深入理解動態代理

常識問題: 字節碼文件是怎麼產生的, 由java源碼編譯出來的. 也就意味着字節碼都有對應的源碼.但是:我們看到代理對象的類名是: com.sun.proxy.$Proxy0. 問題: 該類的源代碼在哪裏? 我們不會寫源碼保存到com.sun.proxy包的.那這個類就沒有源代碼, 而是直接在內存中生成的字節碼文件.磁盤也沒有.

動態代理源碼分析

Proxy類的newProxyInstance方法

@CallerSensitive
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);
        }
        /*
         * 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;//把屬性 h 賦值給ih
            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);
        }
    }

newProxyInstance()方法幫我們執行了生成代理類----獲取構造器----生成代理對象這三步;
生成代理類: Class<?> cl = getProxyClass0(loader, intfs);
獲取構造器: final Constructor<?> cons = cl.getConstructor(constructorParams);
生成代理對象: cons.newInstance(new Object[]{h});
Proxy.getProxyClass0()如何生成代理類? getProxyClass0方法源碼

/**
     * 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
        //cache是緩存的意思, 看緩存中是否有代理對象了
        return proxyClassCache.get(loader, interfaces);
    }

繼續跟蹤 proxyClassCache.get(loader, interfaces);

public V get(K key, P parameter) {// key:類加載器;parameter:接口數組
        // 檢查指定類型的對象引用不爲空null。當參數爲null時,拋出空指針異常。
        Objects.requireNonNull(parameter);
		// 清除已經被GC回收的弱引用
        expungeStaleEntries();
		// 將ClassLoader包裝成CacheKey, 作爲一級緩存的key
        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
		// 根據代理類實現的接口數組來生成二級緩存key
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
		// 通過subKey獲取二級緩存值
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
		// 這個循環提供了輪詢機制, 如果條件爲假就繼續重試直到條件爲真爲止
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
				// 在這裏supplier可能是一個Factory也可能會是一個CacheValue
				// 在這裏不作判斷, 而是在Supplier實現類的get方法裏面進行驗證
                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實例作爲subKey對應的值
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
 
            if (supplier == null) {
			    // 到這裏表明subKey沒有對應的值, 就將factory作爲subKey的值放入
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
					// 到這裏表明成功將factory放入緩存
                    supplier = factory;
                }
				// 否則, 可能期間有其他線程修改了值, 那麼就不再繼續給subKey賦值, 而是取出來直接用
                // else retry with winning supplier
            } else {
			    // 期間可能其他線程修改了值, 那麼就將原先的值替換
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
					// 成功將factory替換成新的值
                    supplier = factory;
                } else {
                    // retry with current supplier
					// 替換失敗, 繼續使用原先的值
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

該方法中的Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory調用apply,具體實現在ProxyClassFactory中完成。
ProxyClassFactory.apply()實現代理類創建。

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        // prefix for all proxy class names
	// 統一代理類的前綴名都以$Proxy
        private static final String proxyClassNamePrefix = "$Proxy";
 
        // next number to use for generation of unique proxy class names
	// 使用唯一的編號給作爲代理類名的一部分,如$Proxy0,$Proxy1等
        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.
		 * 驗證指定的類加載器(loader)加載接口所得到的Class對象(interfaceClass)是否與intf對象相同
                 */
                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.
		 * 驗證該Class對象是不是接口
                 */
                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
		/*如果都是public接口,那麼生成的代理類就在com.sun.proxy包下如果報java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class 
		(系統找不到指定的路徑。)的錯誤,就先在你項目中創建com.sun.proxy路徑*/
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
 
            /*
             * Choose a name for the proxy class to generate.
	     * nextUniqueNumber 是一個原子類,確保多線程安全,防止類名重複,類似於:$Proxy0,$Proxy1......
             */
            long num = nextUniqueNumber.getAndIncrement();
	    // 代理類的完全限定名,如com.sun.proxy.$Proxy0.calss
            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());
            }
        }
    }

代理類創建真正在ProxyGenerator.generateProxyClass()方法中,方法簽名如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to generate proxy dispatching code for.
* 步驟1:爲所有方法生成代理調度代碼,將代理方法對象集合起來。
*/
        //增加 hashcode、equals、toString方法
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);
        // 獲得所有接口中的所有方法,並將方法添加到代理方法中
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                addProxyMethod(m, intf);
            }
        }
 
        /*
         * 驗證方法簽名相同的一組方法,返回值類型是否相同;意思就是重寫方法要方法簽名和返回值一樣
         */
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            checkReturnTypes(sigmethods);
        }
 
        /* ============================================================
         * Step 2: Assemble FieldInfo and MethodInfo structs for all of fields and methods in the class we are generating.
         * 爲類中的方法生成字段信息和方法信息
         */
        try {
            // 生成代理類的構造函數
            methods.add(generateConstructor());
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {
                    // add static field for method's Method object
                    fields.add(new FieldInfo(pm.methodFieldName,
                            "Ljava/lang/reflect/Method;",
                            ACC_PRIVATE | ACC_STATIC));
                    // generate code for proxy method and add it
					// 生成代理類的代理方法
                    methods.add(pm.generateMethod());
                }
            }
            // 爲代理類生成靜態代碼塊,對一些字段進行初始化
            methods.add(generateStaticInitializer());
        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }
 
        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }
 
        /* ============================================================
         * Step 3: Write the final class file.
         * 步驟3:編寫最終類文件
         */
        /*
         * Make sure that constant pool indexes are reserved for the following items before starting to write the final class file.
         * 在開始編寫最終類文件之前,確保爲下面的項目保留常量池索引。
         */
        cp.getClass(dotToSlash(className));
        cp.getClass(superclassName);
        for (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.getName()));
        }
 
        /*
         * Disallow new constant pool additions beyond this point, since we are about to write the final constant pool table.
         * 設置只讀,在這之前不允許在常量池中增加信息,因爲要寫常量池表
         */
        cp.setReadOnly();
 
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);
 
        try {
            // u4 magic;
            dout.writeInt(0xCAFEBABE);
            // u2 次要版本;
            dout.writeShort(CLASSFILE_MINOR_VERSION);
            // u2 主版本
            dout.writeShort(CLASSFILE_MAJOR_VERSION);
 
            cp.write(dout);             // (write constant pool)
 
            // u2 訪問標識;
            dout.writeShort(accessFlags);
            // u2 本類名;
            dout.writeShort(cp.getClass(dotToSlash(className)));
            // u2 父類名;
            dout.writeShort(cp.getClass(superclassName));
            // u2 接口;
            dout.writeShort(interfaces.length);
            // u2 interfaces[interfaces_count];
            for (Class<?> intf : interfaces) {
                dout.writeShort(cp.getClass(
                        dotToSlash(intf.getName())));
            }
            // u2 字段;
            dout.writeShort(fields.size());
            // field_info fields[fields_count];
            for (FieldInfo f : fields) {
                f.write(dout);
            }
            // u2 方法;
            dout.writeShort(methods.size());
            // method_info methods[methods_count];
            for (MethodInfo m : methods) {
                m.write(dout);
            }
            // u2 類文件屬性:對於代理類來說沒有類文件屬性;
            dout.writeShort(0); // (no ClassFile attributes for proxy classes)
 
        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }
 
        return bout.toByteArray();
    }

手動生成字節碼文件

生成字節碼文件的方法:
① :配置類的訪問規則, 默認 sun包下的類不允許訪問
選擇項目----右鍵—properties菜單( alt+enter )
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

後面一路OK就可以了.

package com.hanker.dynamicproxy;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class Test {
	public static void main(String[] args) {
		testProxy();
	}
	public static void testProxy() {
		//生成代理對象的字節碼文件
		byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{ProductMangerImpl.class});
		try (FileOutputStream fos = 
				new FileOutputStream(new File("E:\\java20191202\\1.java核心\\第15章 反射\\$Proxy0.class"))) 
		{
			fos.write(bytes);
			fos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}   

生成一個字節碼文件:
在這裏插入圖片描述

反編譯字節碼文件

使用 jd-gui.exe 工具反編譯class文件.
在這裏插入圖片描述

基於CGLIB實現動態代理

CGLIB簡介

CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫,它可以在運行期擴展Java類與實現Java接口。Hibernate用它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。CGLIB是一個強大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如Spring AOP爲他們提供方法的interception(攔截)。CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。當然不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
CGLIB依賴兩個jar包:cglib-3.3.0.jar , asm-7.1.jar; 添加到構建路徑.

CGLIB動態代理

創建業務類,注意這個業務類並沒有實現任何接口

package com.hanker.cglib;
//要代理的業務對象
public class ProductMangerImpl {
	public ProductMangerImpl() {
		System.out.println("創建ProductMangerImpl對象");
	}
	public int addProduct() {
		System.out.println("執行添加商品");		
		return 1;
	}
	//該方法不能被子類覆蓋,Cglib是無法代理final修飾的方法
	public final int deleteProduct(int no) {
		System.out.println("執行刪除商品..."+no);
		return 1001;
	}
}

創建方法攔截器實現類

package com.hanker.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//實現MethodInterceptor接口的方法攔截器
public class MyMethodInterceptor implements MethodInterceptor{
    /**
     * obj:cglib生成的代理對象
     * method:被代理對象方法
     * args:方法入參
     * methodProxy: 代理方法
     */
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("調用方法之前====前置通知=====");
		Object result = methodProxy.invokeSuper(obj, args);
		System.out.println("調用方法之後====後置通知=====");
		return result;
	}

}

生成CGLIB代理對象調用目標方法:

package com.hanker.cglib;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Test {
	public static void main(String[] args) {
		// 指定代理類字節碼文件的路徑
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\cglib");
		// 通過CGLIB動態代理獲取代理對象的過程
		Enhancer  enhancer = new Enhancer();
		// 設置enhancer的父類
		enhancer.setSuperclass(ProductMangerImpl.class);
		// 設置enhancer的回調對象
		enhancer.setCallback(new MyMethodInterceptor());
		// 創建代理對象
		ProductMangerImpl proxy = (ProductMangerImpl) enhancer.create();
		//通過代理對象調用目標方法
		proxy.addProduct();//帶日誌
		int result = proxy.deleteProduct(1);//不帶日誌
		System.out.println(result);
	}
}

執行效果:
在這裏插入圖片描述

CGLIB動態代理源碼分析

實現CGLIB動態代理必須實現MethodInterceptor(方法攔截器)接口,源碼如下:

package net.sf.cglib.proxy;
public interface MethodInterceptor extends Callback{
    /**
     * All generated proxied methods call this method instead of the original method.
     * The original method may either be invoked by normal reflection using the Method object,
     * or by using the MethodProxy (faster).
     * @param obj "this", the enhanced object 
     * @param method intercepted Method
     * @param args argument array; primitive types are wrapped
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @throws Throwable any exception may be thrown; if so, super method will not be invoked
     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
     * @see MethodProxy
     */    
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

這個接口只有一個intercept()方法,這個方法有4個參數:
1)obj表示增強的對象,即實現這個接口類的一個對象;
2)method表示要被攔截的方法;
3)args表示要被攔截方法的參數;
4)proxy表示要觸發父類的方法對象;

在上面的Test代碼中,通過Enhancer.create()方法創建代理對象,create()方法的源碼:

/**
* Generate a new class if necessary and uses the specified
* callbacks (if any) to create a new object instance.
* Uses the no-arg constructor of the superclass.
* @return a new instance
*/
public Object create() {
     classOnly = false;
     argumentTypes = null;
	return createHelper();
}

該方法含義就是如果有必要就創建一個新類,並且用指定的回調對象創建一個新的對象實例,
使用的父類的參數的構造方法來實例化父類的部分。核心內容在createHelper()中,源碼如下:

private Object createHelper() {
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

preValidate()方法校驗callbackTypes、filter是否爲空,以及爲空時的處理。通過newInstance()方法創建EnhancerKey對象,作爲Enhancer父類AbstractClassGenerator.create()方法創建代理對象的參數。

protected Object create(Object key) {
        try {
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            if (data == null) {
                synchronized (AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            return nextInstance(obj);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

真正創建代理對象方法在nextInstance()方法中,該方法爲抽象類AbstractClassGenerator的一個方法,簽名如下:abstract protected Object nextInstance(Object instance) throws Exception;
在子類Enhancer中實現,實現源碼如下:

protected Object nextInstance(Object instance) {
        EnhancerFactoryData data = (EnhancerFactoryData) instance;
        if (classOnly) {
            return data.generatedClass;
        }
        Class[] argumentTypes = this.argumentTypes;
        Object[] arguments = this.arguments;
        if (argumentTypes == null) {
            argumentTypes = Constants.EMPTY_CLASS_ARRAY;
            arguments = null;
        }
        return data.newInstance(argumentTypes, arguments, callbacks);
}

看看data.newInstance(argumentTypes, arguments, callbacks)方法,
第一個參數爲代理對象的構成器類型,第二個爲代理對象構造方法參數,第三個爲對應回調對象。
最後根據這些參數,通過反射生成代理對象,源碼如下:

/**
* Creates proxy instance for given argument types, and assigns the callbacks.
* Ideally, for each proxy class, just one set of argument types should be used,
* otherwise it would have to spend time on constructor lookup.
* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
*
* @see #createUsingReflection(Class)
* @param argumentTypes constructor argument types
* @param arguments constructor arguments
* @param callbacks callbacks to set for the new instance
* @return newly created proxy
*/
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
    setThreadCallbacks(callbacks);
    try {
        // Explicit reference equality is added here just in case Arrays.equals does not have one
        if (primaryConstructorArgTypes == argumentTypes ||
            Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
            // If we have relevant Constructor instance at hand, just call it
            // This skips "get constructors" machinery
            return ReflectUtils.newInstance(primaryConstructor, arguments);
        }
        // Take a slow path if observing unexpected argument types
        return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
    } finally {
        // clear thread callbacks to allow them to be gc'd
        setThreadCallbacks(null);
    }
}

最後生成代理對象:
在這裏插入圖片描述
將其反編譯後代碼如下:

public class ProductMangerImpl$$EnhancerByCGLIB$$e5b0daae 
      extends ProductMangerImpl implements Factory{
  ....
    public final int addProduct(){
    ...
    if (tmp17_14 != null){
      // 調用攔截器
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$addProduct$0$Method, 
                                                 CGLIB$emptyArgs, CGLIB$addProduct$0$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.addProduct();
  }
  ....
}

最後我們總結一下JDK動態代理和Gglib動態代理的區別:
1.JDK動態代理是實現了被代理對象的接口,Cglib是繼承了被代理對象。
2.JDK和Cglib都是在運行期生成字節碼,JDK是直接寫Class字節碼,Cglib使用ASM框架寫Class字節碼,Cglib代理實現更復雜,生成代理類比JDK效率低。
3.JDK調用代理方法,是通過反射機制調用,Cglib是通過FastClass機制直接調用方法,Cglib執行效率更高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章