Java 动态代理介绍及用法
Java 動態代理介紹及用法
一,靜態代理模式的特點
在之前的文章中 java代理模式 已經介紹里java里的(靜態)代理模式
下面是上文靜態代理類的例子:
public class ProxyBear implements Hitable<Dog>{ private Hitable<Dog> f = null; public ProxyBear(){ if (null == f){ f = new Fox(); } } @Override public void hit(Dog g){ if (null != f){ System.out.println("Bear hit InterDogChicken!"); f.hit(g); System.out.println("Bear bite InterDogChicken!"); } }從代碼可以看出, 上面代理類可以增強被代理的對象的某個方法。
但是被代理對象的類型Hitable是指定1個接口(或抽象類)
也就是講上靜態代理類只適用于某一種指定的接口實現類, 如果某個對象沒有實現揍狗接口, 而是其他另1個接口, 那么就必須新建1個代理類的。即使新代理類也是實現同樣的功能
二. 動態代理的1個需求
就用會上文的例子
我們宜家有兩個接口。毆打和調戲接口..
毆打接口 Hitable
public interface Hitable<T> {public void hit(T o); }調戲接口 Molestable
public interface Molestable<T> {public void molest(T o); }其中狐貍類實現了毆打狗接口
public class Fox implements Hitable<Dog> {@Overridepublic void hit(Dog g){this.sap(g);this.uppercut(g);}//悶棍private void sap(Dog g){System.out.println("give " + g.getName() + " a Sap!");}//上勾拳private void uppercut(Dog g){System.out.println("give " + g.getName() + " a Uppercute!");} }狼類實現調戲狗接口
public class Wolf implements Molestable<Dog> {@Overridepublic void molest(Dog o) {System.out.println("wolf laugh at the dog!");System.out.println("wolf ruffle the dog!");} }這個是萬惡的狗類
public class Dog {private String name;public Dog(String name){this.setName(name);}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Dog [name=" + name + "]";} }問題來了, 依家狗太惡, 狼和狐貍都要找頭熊(代理)看著才敢去惹狗
如果用靜態代理, 就如本文一開始的那個代理類, 則只能單獨為狐貍or 狼代理, 這個需求需要兩個代理類, 因為狗和狐貍實現的是兩個不同的接口
如下圖
那么有沒有一種代理類, 可以同時代理多個接口的實現類呢,java 里動態代理就利用了反射實現了這個功能。
三,動態代理的例子
我們利用動態代理類重新寫了1個熊類出來:
熊
public class dynamicProxyBear implements InvocationHandler {//delegate means proxy//the object which will be delegatedprivate Object delegate;public Object bind(Object delegate){this.delegate = delegate;/*** This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class)* * @param * 1.ClassLoader loader -> usually use delegate object's class loader* 2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented* 3.InvocationHandler h -> this* @return*/return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);}/*** This method will replace all the method owned by delegate object.* @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop* @param method -> the method (once execute will fall into this invoke method) object of delegate object * @param args -> parameters of the mothod.* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result;if (args.length < 1){return method.invoke(this.delegate,args);}//bear watchingSystem.out.println("bear is watching " + args[0].toString());result = method.invoke(this.delegate,args);System.out.println("bear leaved " + args[0].toString());return result;} }下面對上面的代碼進行分析。
首先這個代理類要實現接口InvacationHandler, 而且不會直接提供要代理的方法(行為), 而是生成1個被代理完成(強化)的新對象。
delegate 成員對象
//delegate means proxy//the object which will be delegatedprivate Object delegate;這個成員變量就是為了存放被代理的對象(Fox or wolf 對象)
bind(Object delegate) 方法
public Object bind(Object delegate){this.delegate = delegate;/*** This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class)* * @param * 1.ClassLoader loader -> usually use delegate object's class loader* 2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented* 3.InvocationHandler h -> this* @return*/return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);}這個bind方法就是為了綁定要代理的對象, 并return 1個被代理后的對象啦
注意 參數Object delegate就是本文例子中的狼or狐貍對象, 因為參數類型是Object, 也就是說它可以同時適用于各種類(前提是實現了某種接口)
Proxy.newProxyInstance 這個靜態方法就是返回1個被代理后的對象。
第一個參數是類加載器, 通常使用被代理對象的類加載器, 通常我們就用delegate.getClass().getClassLoader()
第二個參數是被代理對象實現的接口列表, 通常我們就用delegate.getClass().getInterface()
第三個參數是這個代理類對象本身
值得注意的是, 這個方法返回的也是1個Object類型, 在client端中, 我們可以將返回對象強制轉換成被代理類的1個接口類型對象, 但是不能強制轉換成被代理類本身的1個對象。
至于這個靜態方法里面的細節這里就不講了。
重寫方法invoke
/*** This method will replace all the method owned by delegate object.* @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop* @param method -> the method (once execute will fall into this invoke method) object of delegate object * @param args -> parameters of the mothod.* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result;if (args.length < 1){return method.invoke(this.delegate,args);}//bear watchingSystem.out.println("bear is watching " + args[0].toString());result = method.invoke(this.delegate,args);System.out.println("bear leaved " + args[0].toString());return result;} }這個方法就是真正實現代理功能的關鍵,
它有三個參數
Object proxy對象代表被代理的對象
Method method代表要被代理的方法(實際上是所有方法)
args 就是代表方法的參數列表了。
返回值Object result 自然就是代表被代理方法的返回值了。
我們在里面先加入 熊看著狗的代碼…
然后執行被代理的方法本身
然后加入熊離開的代碼。
最后要返回被代理方法的返回值。
而且, 千萬不要直接調用proxy的方法, 因為proxy直接調用方法會再次進入這個代理對象invoke方法, 導致死循環。
Client端代碼
public static void g(){Dog g = new Dog("InterDogChicken");dynamicProxyBear bear = new dynamicProxyBear();Hitable<Dog> newFox = (Hitable<Dog>) bear.bind(new Fox());newFox.hit(g);Molestable<Dog> newWolf = (Molestable<Dog>) bear.bind(new Wolf());newWolf.molest(g);}上面代碼中 首先要定義1個動態代理(熊)類, 然后熊返回1個新的狐貍對象(必須強制轉換為Fox or Wolf類的1個接口, 而不能是本身)
然后再執行這個新的狐貍對象的方法。
輸出結果
bear is watching Dog [name=InterDogChicken] give InterDogChicken a Sap! give InterDogChicken a Uppercute! bear leaved Dog [name=InterDogChicken] bear is watching Dog [name=InterDogChicken] wolf laugh at the dog! wolf ruffle the dog! bear leaved Dog [name=InterDogChicken]這樣的話無論狐貍or狼都被代理了
四,動態代理的關鍵點和小結
值得注意是, 上面重寫的invoke的方法, 我并沒有提到Fox or 狼類的方法名字。
實際上, 動態代理會利用java反射機制攔截被代理對象的所有方法。 接管對方法里管理。
所以動態代理是非常適合增加日志監控功能的。
例如在每個方法執行前和執行后都加日志的話, 動態代理模式避免了去修改以前的業務類。
被代理對象必須至少實現1個接口才能被動態代理對象代理, 因為動態代理bind方法返回的對象必須強制轉換成1個接口引用。
而且,動態代理只對被代理對象的第一層入口方法有效果。
例如下面這個對象有3個方法
void a(){}void b(){}void c(){a();b(); }那么無論動態代理對象執行 a b c那個方法, 都是被攔截一次, 而不是當執行c時, c a b分別被攔截3次
五, 另1個例子, 利用動態方法實現監控功能
動態代理有時也會被利用于接管被代理對象的方法, 實現監控功能
例如下面要實現1個學生類, 它一次執行study/exam/graduate 三個方法,
學習接口:
public interface Studyable {public void study();public int exam();public void graduate(); }學生類
package proxyPattern.dynamicProxy;public class Student implements Studyable {private String name;private int examResult;public Student(String name, int examResult) {super();this.name = name;this.examResult = examResult;}@Overridepublic String toString() {return "Student [name=" + name + ", examResult=" + examResult + "]";}@Overridepublic void study() {System.out.println(this + " is studying");}@Overridepublic int exam() {System.out.println(this + " is examming");return this.examResult;}@Overridepublic void graduate() {System.out.println(this + " graduated");} }但是我們加入一個要求, 就是當某個學生對象exam分數少于60時, graduate執行失敗
有人說直接修改學生類最快, 但是項目中很多情況下你沒有修改上層類的權限, 這個時候我們可以利用動態代理來實現:
動態代理類 StudentMonitor
package proxyPattern.dynamicProxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class StudentMonitor implements InvocationHandler{private Object delegate;private boolean flag = false;public Object bind(Object delegate){this.delegate = delegate;return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("start to execute " + method.getName());Object result;if(method.getName().equals("exam")){result = method.invoke(this.delegate, args);if ((Integer)result >= 60){this.flag = true;}else{this.flag = false;}return result;}if(method.getName().equals("graduate")){if(this.flag == true){return method.invoke(this.delegate, args);}System.out.println(this.delegate + " exam result < 60,cannot graduate");return null;}return method.invoke(this.delegate, args);}}在重寫的invoke方法中, 我們可以加入方法名字判斷實現我們的需求
client代碼
public static void h(){StudentMonitor sm = new StudentMonitor();//must be translated to a interface object of the class, cannot be translate to a class objectStudyable st = (Studyable)sm.bind(new Student("Mike", 59));st.study();st.exam();st.graduate();Studyable st2 = (Studyable)sm.bind(new Student("bill", 61));st2.study();st2.exam();st2.graduate();}輸出
start to execute study Student [name=Mike, examResult=59] is studying start to execute exam Student [name=Mike, examResult=59] is examming start to execute graduate Student [name=Mike, examResult=59] exam result < 60,cannot graduate start to execute study Student [name=bill, examResult=61] is studying start to execute exam Student [name=bill, examResult=61] is examming start to execute graduate Student [name=bill, examResult=61] graduated總結
以上是生活随笔為你收集整理的Java 动态代理介绍及用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring xml 配置使用外部con
- 下一篇: MyEclipse创建JavaWeb应用