Java中的代理的使用
Java中的代理
代理解釋:一種設計模式,代理類與委托類(被代理對象、目標對象)有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
通俗解釋:不直接調運目標對象,直接調運代理對象,讓代理對象去調運目標對象。
代理目的:當調運代理對象,然后在代理對象調運目標對象之前或者調運目標對象以后,我們可以干一些事,比如權限控制,日志記錄等。
代理分類:靜態代理,動態代理
舉例分析:
程序說明:有剛學說話的個小朋友想說“我是中國人”,但是他不能完整的說出來,只能說“中國”兩個字,那么我們就使用代理模式,讓他的爸爸媽媽幫他把他想說的話補充完整。他的爸爸幫他說:“我是”,他的媽媽幫他說:“人”。然后即使小朋友只說了“中國”,但是我們聽見的就是“我是中國人”。
這其中小朋友就是被代理對象、目標對象,他的爸爸媽媽就是代理對象。
首先建立一個Person接口:
public interface Person {
public void say();
}
建立一個接口的實現類:
public class PersonImpl implements Person {
@Override
public void say() {
System.out.print("中國");
}
}
再建立一個沒有實現接口的類:
public class PersonNoImpl {
public void say() {
System.out.print("中華人民共和國");
}
}
1.靜態代理:在程序運行前,代理類的.class文件就已經存在了。
public class PersonProxy implements Person {
private Person person;// 被代理對象
public PersonProxy(Person p) {
this.person = p;
}
@Override
public void say() {
System.out.print("我是-");
person.say();// 在目標方法前后分別添加操作
System.out.println("-人");
}
}</pre>
這種方式的最大缺點就是每次我們都需要建立不同的代理對象,靈活性和可復用性都很差,所以我們需要使用到動態代理技術。
2.動態代理:在程序運行時,運用反射機制動態創建而成。
有兩種實現方式:JDK代理,CGLIB代理
方式1:使用jdk提供的代理對象(代理對象需要實現InvocationHandler接口,目標對象必須實現其他接口)
public class PersonProxyJdk implements InvocationHandler {
private Object targetObj;
/
@param obj
被代理對象
@return 代理對象的實例
*/
public Object createProxyInstance(Object obj) {
this.targetObj = obj;
/
參數說明 參數一:ClassLoader,定義代理類的類加載器
參數二:Class<?>,代理類要實現的接口列表
* 參數三:InvocationHandler,指派方法調用的調用處理程序 (指定調運哪個類的invoke方法)
**/
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), this.targetObj.getClass().getInterfaces(), this);
}
/
當代理對象的方法被調運時,就會執行回調函數invoke方法,讓這個回調函數再去執行目標代碼的指定方法,
并且會將代理對象接收到的參數傳遞給目標代碼。 這是一個回調函數。
注意:method args 都是由調運代理對象后產生的。所以是確定的。
還可以在這個回調函數中做一些手腳,比如限制調運或者其他。
/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.print("我是");
result = method.invoke(targetObj, args);
System.out.println("人");
return result;
}
}</pre>
注意:目標對象必須實現接口。因為需要得到接口的所有方法,然后進行調運接口的所有方法。
方式2:使用cglib提供的代理對象(代理對象需要實現MethodInterceptor接口,目標對象不需要實現接口)
//導包:cglib-nodep-2.2.3.jar
public class PersonProxyCglib implements MethodInterceptor {
private Object targetObj;
/*
生成的代理對象其實就是目標對象的子類
@param obj
被代理對象
@return 代理對象的實例
/
public Object createProxyInstance(Object obj) {
this.targetObj = obj;
Enhancer enhancer = new Enhancer();// 用于生成代理對象
enhancer.setSuperclass(this.targetObj.getClass());// 設置代理對象的父類
enhancer.setCallback(this);// 設置代理對象的回調函數就是本身
return enhancer.create();// 生成代理對象
}
/
當代理對象的方法被調運時,就會執行改代理對象的回調函數,也就是intercept方法 這個回調函數接收代理對象傳遞來的參數
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result = null;
System.out.print("我是#");
result = proxy.invokeSuper(obj, args);
System.out.print("#人");
return result;
}
}</pre>
注意:目標對象不須實現接口。因為生成的代理對象時目標對象的子類。
3.測試上面的代理
public class Main {
public static void main(String[] args) {
Person zhangsan = new PersonImpl();// 被代理的對象
zhangsan.say();// 如果讓代理目標直接執行目標方法,就不能在目標方法前后操作
System.out.println();
PersonProxy proxy = new PersonProxy(zhangsan);// 將被代理的對象傳遞一個代理類
proxy.say();// 讓代理類去執行目標方法,這個時候代理類就在目標方法執行前后亂搞了
PersonProxyJdk proxyJdk = new PersonProxyJdk();// 創建一個代理對象
Person zhangsanJdk = (Person) proxyJdk.createProxyInstance(zhangsan);// 將被代理對象傳遞給代理對象,并且返回被代理接口
zhangsanJdk.say();// 調運被代理對象的接口,就能動態的去執行代理對象想要執行的操作
PersonProxyCglib proxyCglib = new PersonProxyCglib();// 創建Cglib代理對象
Person zhansanCglib = (Person) proxyCglib.createProxyInstance(zhangsan);// 將被代理對象傳遞給代理對象,并且返回被代理接口
zhansanCglib.say();// 調運被代理對象的接口,就能動態的去執行代理對象想要執行的操作
// 使用Cglib的時候,目標對象可以不實現任何接口,但是使用JDK代理的時候就不可以
PersonNoImpl lisi = new PersonNoImpl();
PersonNoImpl lisiCglib = (PersonNoImpl) proxyCglib.createProxyInstance(lisi);
lisiCglib.say();
}
}
輸出:
中國
我是-中國-人
我是中國人
我是#中國#人
我是#中華人民共和國#人</pre>來自:http://dyygusi.iteye.com/blog/1994843