吃透動態代理,解密spring AOP原始碼(二)
緊接著上節,為了解決靜態代理的問題,出現了動態代理,先上程式碼:
/** * 動態代理 */ public class DynamicProxyCompanyC implements InvocationHandler { // 被代理的物件,即真實物件 private Object factory; public Object getFactory() { return factory; } public void setFactory(Object factory) { this.factory = factory; } // 通過proxy獲取動態代理的物件 public Object getProxyInstance() { //第三個引數是InvocationHandler,傳入自身說明此proxy物件是和自身的invoke方法合作的,代理物件方法呼叫會經過下面invoke的增強 return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this); } @Override /**通過動態代理物件對方法進行增強 *@param proxy 代理物件 *@param method 要增強的方法(攔截的方法) *@param args 方法引數 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { dosomeThingBefore(); Object ret = method.invoke(factory, args);// 通過反射機制呼叫方法 dosomeThingAfter(); return ret; } public void dosomeThingBefore() { System.out.println("售前服務,負責產品調研,興趣愛好"); } public void dosomeThingAfter() { System.out.println("售後服務,包裝丶送貨上門一條龍服務"); } }
假設動態代理是一個代購公司,私有變數Object factory為動態生成的具體的真實物件,可代購對應的產品
測試類:
public class Proxytest { public static void main(String[] args) { // 代購公司C,負責代購所有產品 DynamicProxyCompanyC proxy = new DynamicProxyCompanyC(); // 日本有家A公司生產男性用品 ManToolFactory dogToolFactory = new AManFactory(); // 代購A公司的產品 proxy.setFactory(dogToolFactory); // 建立A公司的代理物件 ManToolFactory proxyObject = (ManToolFactory) proxy.getProxyInstance(); // 代理物件完成代購男性用品 proxyObject.saleManTool("D"); System.out.println("--------------"); // 日本有家B公司生產女性用品 WomanToolFactory womanToolFactory = new BWomanFactory(); // 代購B公司的產品 proxy.setFactory(womanToolFactory); // 建立B公司的代理物件 WomanToolFactory proxyObject1 = (WomanToolFactory) proxy.getProxyInstance(); // 代理物件完成代購女性用品 proxyObject1.saleWomanTool(1.8); } } // 售前服務,負責產品調研,興趣愛好 // A工廠出售男性用品,D罩杯 // 售後服務,包裝丶送貨上門一條龍服務 // -------------- // 售前服務,負責產品調研,興趣愛好 // B工廠生產女性用品,長度1.8米 // 售後服務,包裝丶送貨上門一條龍服務
動態代理解決了上節說的開閉原則,那麼接下來我們要解密動態代理的原理,重點類DynamicProxyCompanyC :1.實現了InvocationHandler介面;2.通過proxy獲取動態代理的物件。
根據我們此例子裡面來說,動態代理就類似一個代購公司,可代購所有產品,需要購買哪個產品的時候就例項化一個真實物件(如測試類需要男性用品則將介面引用指向真實物件AManFactory),根據真實物件建立代理物件來執行具體的方法,圖解如下:
Proxy:
接下來我們先初步看一下JDK裡面的Proxy這個原始碼
這個註釋是說Proxy提供個一個靜態方法來建立代理類和代理例項,它也是所有由此方法建立的代理類的父類。
靜態方法建立代理例項即方法newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h);
InvocationHandler:
InvocationHandler 是一個介面,定義了invoke(Object proxy, Method method, Object[] args)方法
總的來說Proxy專門負責new一個例項(真實物件),而具體方法做什麼,業務怎樣增強就由InvocationHandler(抽象物件)的invoke方法(抽象物件即介面定義的方法)來決定。
接下來我們要搞清楚動態代理的底層原理,首先我們除錯一下,會發現 ManToolFactory proxyObject = (ManToolFactory) proxy.getProxyInstance()中建立的proxyObject 物件類名是&Proxy0,是ManToolFactory介面的實現類。但是我們專案工程裡面卻沒有&Proxy0這個類,那它究竟是怎麼出現的,下節講解。