Java 动态代理介绍及用法
Java 動(dòng)態(tài)代理介紹及用法
一,靜態(tài)代理模式的特點(diǎn)
在之前的文章中 java代理模式 已經(jīng)介紹里java里的(靜態(tài))代理模式
下面是上文靜態(tài)代理類的例子:
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!"); } }從代碼可以看出, 上面代理類可以增強(qiáng)被代理的對(duì)象的某個(gè)方法。
但是被代理對(duì)象的類型Hitable是指定1個(gè)接口(或抽象類)
也就是講上靜態(tài)代理類只適用于某一種指定的接口實(shí)現(xiàn)類, 如果某個(gè)對(duì)象沒(méi)有實(shí)現(xiàn)揍狗接口, 而是其他另1個(gè)接口, 那么就必須新建1個(gè)代理類的。即使新代理類也是實(shí)現(xiàn)同樣的功能
二. 動(dòng)態(tài)代理的1個(gè)需求
就用會(huì)上文的例子
我們宜家有兩個(gè)接口。毆打和調(diào)戲接口..
毆打接口 Hitable
public interface Hitable<T> {public void hit(T o); }調(diào)戲接口 Molestable
public interface Molestable<T> {public void molest(T o); }其中狐貍類實(shí)現(xiàn)了毆打狗接口
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!");} }狼類實(shí)現(xiàn)調(diào)戲狗接口
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!");} }這個(gè)是萬(wàn)惡的狗類
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 + "]";} }問(wèn)題來(lái)了, 依家狗太惡, 狼和狐貍都要找頭熊(代理)看著才敢去惹狗
如果用靜態(tài)代理, 就如本文一開始的那個(gè)代理類, 則只能單獨(dú)為狐貍or 狼代理, 這個(gè)需求需要兩個(gè)代理類, 因?yàn)楣泛秃倢?shí)現(xiàn)的是兩個(gè)不同的接口
如下圖
那么有沒(méi)有一種代理類, 可以同時(shí)代理多個(gè)接口的實(shí)現(xiàn)類呢,java 里動(dòng)態(tài)代理就利用了反射實(shí)現(xiàn)了這個(gè)功能。
三,動(dòng)態(tài)代理的例子
我們利用動(dòng)態(tài)代理類重新寫了1個(gè)熊類出來(lái):
熊
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;} }下面對(duì)上面的代碼進(jìn)行分析。
首先這個(gè)代理類要實(shí)現(xiàn)接口InvacationHandler, 而且不會(huì)直接提供要代理的方法(行為), 而是生成1個(gè)被代理完成(強(qiáng)化)的新對(duì)象。
delegate 成員對(duì)象
//delegate means proxy//the object which will be delegatedprivate Object delegate;這個(gè)成員變量就是為了存放被代理的對(duì)象(Fox or wolf 對(duì)象)
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);}這個(gè)bind方法就是為了綁定要代理的對(duì)象, 并return 1個(gè)被代理后的對(duì)象啦
注意 參數(shù)Object delegate就是本文例子中的狼or狐貍對(duì)象, 因?yàn)閰?shù)類型是Object, 也就是說(shuō)它可以同時(shí)適用于各種類(前提是實(shí)現(xiàn)了某種接口)
Proxy.newProxyInstance 這個(gè)靜態(tài)方法就是返回1個(gè)被代理后的對(duì)象。
第一個(gè)參數(shù)是類加載器, 通常使用被代理對(duì)象的類加載器, 通常我們就用delegate.getClass().getClassLoader()
第二個(gè)參數(shù)是被代理對(duì)象實(shí)現(xiàn)的接口列表, 通常我們就用delegate.getClass().getInterface()
第三個(gè)參數(shù)是這個(gè)代理類對(duì)象本身
值得注意的是, 這個(gè)方法返回的也是1個(gè)Object類型, 在client端中, 我們可以將返回對(duì)象強(qiáng)制轉(zhuǎn)換成被代理類的1個(gè)接口類型對(duì)象, 但是不能強(qiáng)制轉(zhuǎn)換成被代理類本身的1個(gè)對(duì)象。
至于這個(gè)靜態(tài)方法里面的細(xì)節(jié)這里就不講了。
重寫方法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;} }這個(gè)方法就是真正實(shí)現(xiàn)代理功能的關(guān)鍵,
它有三個(gè)參數(shù)
Object proxy對(duì)象代表被代理的對(duì)象
Method method代表要被代理的方法(實(shí)際上是所有方法)
args 就是代表方法的參數(shù)列表了。
返回值Object result 自然就是代表被代理方法的返回值了。
我們?cè)诶锩嫦燃尤?熊看著狗的代碼…
然后執(zhí)行被代理的方法本身
然后加入熊離開的代碼。
最后要返回被代理方法的返回值。
而且, 千萬(wàn)不要直接調(diào)用proxy的方法, 因?yàn)閜roxy直接調(diào)用方法會(huì)再次進(jìn)入這個(gè)代理對(duì)象invoke方法, 導(dǎo)致死循環(huán)。
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個(gè)動(dòng)態(tài)代理(熊)類, 然后熊返回1個(gè)新的狐貍對(duì)象(必須強(qiáng)制轉(zhuǎn)換為Fox or Wolf類的1個(gè)接口, 而不能是本身)
然后再執(zhí)行這個(gè)新的狐貍對(duì)象的方法。
輸出結(jié)果
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]這樣的話無(wú)論狐貍or狼都被代理了
四,動(dòng)態(tài)代理的關(guān)鍵點(diǎn)和小結(jié)
值得注意是, 上面重寫的invoke的方法, 我并沒(méi)有提到Fox or 狼類的方法名字。
實(shí)際上, 動(dòng)態(tài)代理會(huì)利用java反射機(jī)制攔截被代理對(duì)象的所有方法。 接管對(duì)方法里管理。
所以動(dòng)態(tài)代理是非常適合增加日志監(jiān)控功能的。
例如在每個(gè)方法執(zhí)行前和執(zhí)行后都加日志的話, 動(dòng)態(tài)代理模式避免了去修改以前的業(yè)務(wù)類。
被代理對(duì)象必須至少實(shí)現(xiàn)1個(gè)接口才能被動(dòng)態(tài)代理對(duì)象代理, 因?yàn)閯?dòng)態(tài)代理bind方法返回的對(duì)象必須強(qiáng)制轉(zhuǎn)換成1個(gè)接口引用。
而且,動(dòng)態(tài)代理只對(duì)被代理對(duì)象的第一層入口方法有效果。
例如下面這個(gè)對(duì)象有3個(gè)方法
void a(){}void b(){}void c(){a();b(); }那么無(wú)論動(dòng)態(tài)代理對(duì)象執(zhí)行 a b c那個(gè)方法, 都是被攔截一次, 而不是當(dāng)執(zhí)行c時(shí), c a b分別被攔截3次
五, 另1個(gè)例子, 利用動(dòng)態(tài)方法實(shí)現(xiàn)監(jiān)控功能
動(dòng)態(tài)代理有時(shí)也會(huì)被利用于接管被代理對(duì)象的方法, 實(shí)現(xiàn)監(jiān)控功能
例如下面要實(shí)現(xiàn)1個(gè)學(xué)生類, 它一次執(zhí)行study/exam/graduate 三個(gè)方法,
學(xué)習(xí)接口:
public interface Studyable {public void study();public int exam();public void graduate(); }學(xué)生類
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");} }但是我們加入一個(gè)要求, 就是當(dāng)某個(gè)學(xué)生對(duì)象exam分?jǐn)?shù)少于60時(shí), graduate執(zhí)行失敗
有人說(shuō)直接修改學(xué)生類最快, 但是項(xiàng)目中很多情況下你沒(méi)有修改上層類的權(quán)限, 這個(gè)時(shí)候我們可以利用動(dòng)態(tài)代理來(lái)實(shí)現(xiàn):
動(dòng)態(tài)代理類 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方法中, 我們可以加入方法名字判斷實(shí)現(xiàn)我們的需求
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總結(jié)
以上是生活随笔為你收集整理的Java 动态代理介绍及用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring xml 配置使用外部con
- 下一篇: Spring 利用FactoryBean