標籤:his imp 內容 cat unit new handler cto down
Java動態代理為什麼使用動態代理
當需要對某個類的某個方法進行修飾(增強)的時候,可以使用繼承、裝飾者模式和動態代理。
三種方式局限性:
- 繼承:增強對象不能改變,增強內容不能改變。
- 裝飾者模式:增強對象不能改變,增強內容能改變。
- 動態代理:增強對象可以改變,增強內容可以改變。
使用動態代理可以更加靈活地提高代碼的複用程度。
舉個栗子:
對於電腦教師這個類,有個teach()方法,時過境遷,舊的教學模式可能已經不適合高速發展的電腦行業了,因此在使用這個teach()方法可能需要前面或者後面要增加一點東西。但是對於老舊的項目,我們很難去理解前人的寫法和思維方式,因此不能輕易修改他們的代碼,所以我們可以使用代理去實現對teach()方法的增強(我這裡分為前置增強和後置增強)。
如何使用動態代理Object Proxy.newInstance(ClassLoader loader, Class[] interfaces, InvocationHandler ih)
- loader:類載入器,將類載入到記憶體當中。
- interfaces:代理對象要實現的介面。
- ih:InvocationHandler,其中實現invoke()方法,來實現對目標對象的增強。
- 傳回值:jvm運行時動態產生的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組介面的類型,而是在運行是動態產生的一個對象
InvocationHandler介面
該介面需要實現invoke()方法。
Object invoke(Object proxy, Method method, Object[] args)
- proxy:代理對象。
- method:代理對象使用的目標方法。
- args:目標方法的參數。
- 傳回值:method方法的傳回值。
在使用代理對象使用某個方法的時候,會調用組建代理程式對象時候傳入的InvocationHandler的invoke()方法,因此可以在這個invoke()裡面寫需要增強內容。
Demo
先給出兩個介面和一個實作類別
public interface Subject { public int outPut(int num);}
public interface BSubject { public int outPut2(int num);}
public class realSubject implements Subject, BSubject { @Override public int outPut(int num) { System.out.println("outPut " + String.valueOf(num) + " people"); return num; } @Override public int outPut2(int num) { System.out.println("outPut2 " + String.valueOf(num) + " people"); return num; }}
使用動態代理
前置增強介面:
public interface BeforeAdvance { // 前置增強介面 public void before();}
後置增強介面:
public interface AfterAdvance { // 後置增強介面 public void after();}
建立代理工廠,可以更加方便調用:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyFactory { private Object targetObject; // 目標代理對象 private BeforeAdvance beforeAdvance; // 前置增強 private AfterAdvance afterAdvance; // 後置增強 public Object createFactory() { ClassLoader loader = this.getClass().getClassLoader(); // 載入.class到記憶體 Class[] interfaces = targetObject.getClass().getInterfaces(); // 得到realSubject所有介面(Waiter), 給代理對象提供了一組介面,這個代理對象就會實現這組介面, InvocationHandler ih = new InvocationHandler() { @Override // proxy 是代理對象,是在jvm運行時動態產生的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組介面的類型,而是在運行是動態產生的一個對象 // Method 是目標對象需要增強方法 // args是實參 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(beforeAdvance != null) { // 調用前置增強方法 beforeAdvance.before(); } Object result = method.invoke(targetObject, args); // 通過反射調用目標代理對象的目標方法, result是目標方法的傳回值 if(afterAdvance != null) { // 調用後置增強方法 afterAdvance.after(); } return result; } }; return Proxy.newProxyInstance(loader, interfaces, ih); // 得到一個代理對象 } public AfterAdvance getAfterAdvance() { return afterAdvance; } public void setAfterAdvance(AfterAdvance afterAdvance) { this.afterAdvance = afterAdvance; } public BeforeAdvance getBeforeAdvance() { return beforeAdvance; } public void setBeforeAdvance(BeforeAdvance beforeAdvance) { this.beforeAdvance = beforeAdvance; } public Object getTargetObject() { return targetObject; } public void setTargetObject(Object targetObject) { this.targetObject = targetObject; }}
樣本:
import org.junit.Test;public class Demo { @Test public void fun() { ProxyFactory proxyfactory = new ProxyFactory(); // 代理的目標對象 proxyfactory.setTargetObject(new realSubject()); // 前置增強介面實現 proxyfactory.setBeforeAdvance(new BeforeAdvance() { @Override public void before() { System.out.println("hello"); } }); // 後置增強介面實現 proxyfactory.setAfterAdvance(new AfterAdvance() { @Override public void after() { System.out.println("byebye"); } }); // 產生對Subject介面的實作類別對象 Subject subject = (Subject) proxyfactory.createFactory(); // 通過newInstance()得到一個代理對象,這個代理對象實現了Subject介面,因此可以強轉為Subject介面類。 BSubject subject2 = (BSubject) proxyfactory.createFactory(); Object result = subject.outPut(100); // 對應InvocationHandler的invoke方法:subject為proxy, outPut為method, 參數為args System.out.println(result); System.out.println("-----"); Object result2 = subject2.outPut2(50); System.out.println(result2); }}輸出:hellooutPut 100 peoplebyebye100-----hellooutPut2 50 peoplebyebye50
在這裡面,只要使用反射,method.invoke()就可以調用代理的目標對象的該方法,然後實現前置增強BeforeAdvance介面和後置增強AfterAdvance介面,就可以實現對方法內容的增強了。
其中不同介面的不同實現方法都可以重用BeforeAdvance和AfterAdvance。
對於不同的增強對象也可以使用,只要targetObject改一下就可以了。
附上一篇講的不錯的文章
Java學習之動態代理