代理(Proxy)是一種設(shè)計模式宋距,通俗的講就是通過別人達到自己不可告人的目的(玩笑)轴踱。
舉個例子如下圖:
代理模式的關(guān)鍵點是:代理對象與目標(biāo)對象.代理對象是對目標(biāo)對象的擴展,并會調(diào)用目標(biāo)對象
這三個代理模式,就像是更新?lián)Q代谚赎,越來越先進淫僻。動態(tài)代理解決了靜態(tài)代理必須同目標(biāo)對象繼承
同一個接口或類,CGlib解決了動態(tài)代理目標(biāo)對象必須繼承一個接口的問題沸版。
一.靜態(tài)代理
條件:代理對象必須和目標(biāo)對象繼承同一個接口或者類
代碼如下:
/**
* 接口
*/public interface IUserDao {
? ? void save();
}
/**
* 接口實現(xiàn)
* 目標(biāo)對象
*/public class UserDao implements IUserDao {
? ? public void save() {
? ? ? ? System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
? ? }
}
/**
* 代理對象,靜態(tài)代理
*/public class UserDaoProxy implements IUserDao{
? ? //接收保存目標(biāo)對象? ? private IUserDao target;
? ? public UserDaoProxy(IUserDao target){
? ? ? ? this.target=target;
? ? }
? ? public void save() {
? ? ? ? System.out.println("開始事務(wù)...");
? ? ? ? target.save();//執(zhí)行目標(biāo)對象的方法? ? ? ? System.out.println("提交事務(wù)...");
? ? }
}
/**
* 測試類
*/public class App {
? ? public static void main(String[] args) {
? ? ? ? //目標(biāo)對象? ? ? ? UserDao target = new UserDao();
? ? ? ? //代理對象,把目標(biāo)對象傳給代理對象,建立代理關(guān)系? ? ? ? UserDaoProxy proxy = new UserDaoProxy(target);
? ? ? ? proxy.save();//執(zhí)行的是代理的方法? ? }
}
靜態(tài)代理總結(jié):
1.可以做到在不修改目標(biāo)對象的情況下嘁傀,為目標(biāo)對象條件功能兴蒸。
2.缺點:必須同目標(biāo)對象繼承同一接口或類
二.動態(tài)代理(JDK代理)
不需要實現(xiàn)接口视粮,代理對象的生成是利用JDK的API,在內(nèi)存中生存代理對象橙凳。但是目標(biāo)對象必須繼承一個接口
代理對象的包:java.lang.reflect.Proxy
JDK實現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數(shù),完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
三個參數(shù)分別表示:
ClassLoader loader,:指定當(dāng)前目標(biāo)對象使用類加載器,獲取加載器的方法是固定的
Class<?>[] interfaces,:目標(biāo)對象實現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
InvocationHandler h:事件處理,執(zhí)行目標(biāo)對象的方法時,會觸發(fā)事件處理器的方法,會把當(dāng)前執(zhí)行目標(biāo)對象的方法作為參數(shù)傳入
代碼示例如下:
代理工廠類 ProxyFactory.java
/**
* 創(chuàng)建動態(tài)代理對象
* 動態(tài)代理不需要實現(xiàn)接口,但是需要指定接口類型
*/public class ProxyFactory{
? ? //維護一個目標(biāo)對象? ? private Object target;
? ? public ProxyFactory(Object target){
? ? ? ? this.target=target;
? ? }
? //給目標(biāo)對象生成代理對象? ? public Object getProxyInstance(){
? ? ? ? return Proxy.newProxyInstance(
? ? ? ? ? ? ? ? target.getClass().getClassLoader(),
? ? ? ? ? ? ? ? target.getClass().getInterfaces(),
? ? ? ? ? ? ? ? new InvocationHandler() {
? ? ? ? ? ? ? ? ? ? @Override? ? ? ? ? ? ? ? ? ? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("開始事務(wù)2");
? ? ? ? ? ? ? ? ? ? ? ? //執(zhí)行目標(biāo)對象方法? ? ? ? ? ? ? ? ? ? ? ? Object returnValue = method.invoke(target, args);
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("提交事務(wù)2");
? ? ? ? ? ? ? ? ? ? ? ? return returnValue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? );
? ? }
}
測試類 App.java
/**
* 測試類
*/public class App {
? ? public static void main(String[] args) {
? ? ? ? // 目標(biāo)對象? ? ? ? IUserDao target = new UserDao();
? ? ? ? // 【原始的類型 class cn.itcast.b_dynamic.UserDao】? ? ? ? System.out.println(target.getClass());
? ? ? ? // 給目標(biāo)對象蕾殴,創(chuàng)建代理對象? ? ? ? IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
? ? ? ? // class $Proxy0? 內(nèi)存中動態(tài)生成的代理對象? ? ? ? System.out.println(proxy.getClass());
? ? ? ? // 執(zhí)行方法? 【代理對象】? ? ? ? proxy.save();
? ? }
}
3.Cglib代理
Cglib代理,目標(biāo)對象不需要繼承一個接口岛啸,它是已目標(biāo)子類的方式實現(xiàn)代理的钓觉,所以Cglib代理也叫子類代理
Cglib子類代理的實現(xiàn)方法:
1.導(dǎo)包maven地址:
<dependency>
? ? <groupId>cglib</groupId>
? ? <artifactId>cglib</artifactId>
? ? <version>3.2.10</version>
</dependency>
或者引入pring-core-3.2.5.jar 包
2.在內(nèi)存中動態(tài)構(gòu)建子類
注意:代理類不能為 final,否則報錯坚踩,目標(biāo)方法不能為final或static荡灾,否則不會執(zhí)行目標(biāo)方法額外的
方法。Cglib是如何實現(xiàn)代理的,還仔細沒研究批幌,有興趣的可以去看看源碼础锐。
代碼示例:
/**
* 目標(biāo)對象,沒有實現(xiàn)任何接口
*/public class UserDao {
? ? public void save() {
? ? ? ? System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
? ? }
}
/**
* Cglib子類代理工廠
* 對UserDao在內(nèi)存中動態(tài)構(gòu)建一個子類對象
*/public class ProxyFactory implements MethodInterceptor{
? ? //維護目標(biāo)對象? ? private Object target;
? ? public ProxyFactory(Object target) {
? ? ? ? this.target = target;
? ? }
? ? //給目標(biāo)對象創(chuàng)建一個代理對象? ? public Object getProxyInstance(){
? ? ? ? //1.工具類? ? ? ? Enhancer en = new Enhancer();
? ? ? ? //2.設(shè)置父類? ? ? ? en.setSuperclass(target.getClass());
? ? ? ? //3.設(shè)置回調(diào)函數(shù)? ? ? ? en.setCallback(this);
? ? ? ? //4.創(chuàng)建子類(代理對象)? ? ? ? return en.create();
? ? }
? ? @Override? ? public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
? ? ? ? System.out.println("開始事務(wù)...");
? ? ? ? //執(zhí)行目標(biāo)對象的方法? ? ? ? Object returnValue = method.invoke(target, args);
? ? ? ? System.out.println("提交事務(wù)...");
? ? ? ? return returnValue;
? ? }
}
測試類:
/**
* 測試類
*/public class App {
? ? @Test? ? public void test(){
? ? ? ? //目標(biāo)對象? ? ? ? UserDao target = new UserDao();
? ? ? ? //代理對象? ? ? ? UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
? ? ? ? //執(zhí)行代理對象的方法? ? ? ? proxy.save();
? ? }
}
在Spring的AOP編程中,如果加入容器的目標(biāo)對象有實現(xiàn)的接口荧缘,用JDK代理皆警,如果沒有實現(xiàn)接口用Cglib代理
后面會介紹Spring內(nèi)容,掌握動態(tài)代理會更容易理解Spring的AOP編程