代理模式,大家應該都不陌生,很多框架底層都用了代理模式,像spring、mybatis等。雖然大家都聽說過代理模式,但是可能也并不是那么地了解,本文將說一下常用的代理模式。
一、代理模式介紹
代理模式其實就是找替身,要去辦一件事兒,自己不去,找人代替你去,這就是代理模式。在程序中就是,為對象提供一個替身,控制替身去訪問目標對象,這樣做的好處是,除了目標對象能提供的功能外,還可以讓替身多做一些活,即可以擴展目標對象的功能。被代理的可以是遠程對象、創(chuàng)建時開銷很大的對象或者需要安全控制的對象。
代理模式主要分為以下三種:
二、靜態(tài)代理
「1、靜態(tài)代理介紹:」
使用靜態(tài)代理的時候,需要定義接口或者父類,被代理的對象和代理對象需要一起實現(xiàn)相同的接口或者繼承相同的父類。
「2、應用實例:」
- 定義被代理的對象:
TeacherDaoImpl,需要實現(xiàn)TeacherDao - 定義代理對象:
TeacherDaoProxy,也需要實現(xiàn)TeacherDao - 要調用
TeacherDaoImpl方法時,需要先創(chuàng)建TeacherDaoProxy對象,然后創(chuàng)建TeacherDaoImpl對象,將TeacherDaoImpl對象交給TeacherDaoProxy對象,再調相關方法
代碼實現(xiàn):
public interface TeacherDao {
void teach();
}
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是沒妹子的一天(ノへ ̄、)");
}
}
public class TeacherDaoProxy implements TeacherDao {
private TeacherDao target; // 被代理的對象
public TeacherDaoProxy(TeacherDao target){
this.target = target;
}
@Override
public void teach() {
System.out.println("代理開始");
// 這里可以寫一些額外的邏輯,以達到擴展被代理對象的目的,相當于spring的前置通知
target.teach();
// 這里也可以寫一些額外的邏輯,以達到擴展被代理對象的目的,相當于spring的后置通知
System.out.println("代理結束");
}
}
public class Client {
public static void main(String[] args){
// 創(chuàng)建被代理的對象
TeacherDao target = new TeacherDaoImpl();
// 創(chuàng)建代理對象
TeacherDaoProxy proxy = new TeacherDaoProxy(target);
// 通過代理對象調用方法
proxy.teach();
}
}
「3、靜態(tài)代理的優(yōu)缺點:」
- 優(yōu)點:可以在不修改被代理對象的前提下擴展被代理的對象,做一些增強
- 缺點:需要實現(xiàn)相同的接口或者繼承相同的父類,所以代理類會很多,而且如果接口或者父類有改動,代理對象和被代理對象都需要維護
三、動態(tài)代理(JDK代理)
「1、動態(tài)代理介紹:」
代理對象不要實現(xiàn)接口,但是被代理對象還是需要實現(xiàn)接口的。動態(tài)代理對象的生成,利用的是JDK的API,反射包下的Proxy類,動態(tài)地在內存中構建代理對象。
「2、java.lang.reflect.Proxy:」
這個類有一個newProxyInstance方法,該方法接收三個參數(shù),如下:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
「3、應用實例:」
- 定義被代理的對象:TeacherDaoImpl,需要實現(xiàn)TeacherDao
- 定義一個代理工廠ProxyFactory,有一個getProxyInstance方法,需要傳入被代理的對象,然后返回代理對象實例,通過代理對象調用被代理對象的方法
代碼實現(xiàn):
public interface TeacherDao {
void teach();
}
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是沒妹子的一天(ノへ ̄、)");
}
}
public class ProxyFactory {
private Object target; // 被代理的對象
public ProxyFactory(Object target){
this.target = target;
}
// 給被代理的對象生成一個代理對象
public Object getProxyInstance(){
// 參數(shù)1:指定被代理對象的類加載器
// 參數(shù)2:被代理對象實現(xiàn)的接口類型
// 參數(shù)3:事件處理,執(zhí)行被代理對象的方法
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("JDK代理開始");
// 調用方法,args的方法的參數(shù)
Object returnValue = method.invoke(target, args);
System.out.println("JDK代理結束");
// 將執(zhí)行結果return
return returnValue;
}
});
}
}
public class Client {
public static void main(String[] args){
// 創(chuàng)建被代理的對象
TeacherDao target = new TeacherDaoImpl();
// 創(chuàng)建代理對象
TeacherDao proxy = (TeacherDao) new ProxyFactory(target).getProxyInstance();
// 通過代理對象調用被代理對象的方法
proxy.teach();
}
}
四、cglib代理
「1、cglib代理介紹:」
靜態(tài)代理和動態(tài)代理,被代理的對象,都需要實現(xiàn)接口,如果一個類沒實現(xiàn)任何接口的,那就要用cglib代理了。cglib代理也叫子類代理,它會在內存中構建一個子類對象,從而實現(xiàn)對被代理對象的擴展。cglib代理底層是通過一個叫ASM的字節(jié)碼處理框架來轉換字節(jié)碼并生成新的類從而實現(xiàn)代理的。被代理的類不能為final,否則會報錯。被代理對象的方法如果是final/static,就不會被攔截,即不會執(zhí)行被代理對象額外的業(yè)務方法。
「2、應用實例:」
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是沒妹子的一天(ノへ ̄、)");
}
}
// 需要實現(xiàn)MethodInterceptor并重寫其方法
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target){
this.target = target;
}
/**
* 返回target的代理對象
* @return
*/
public Object getProxyInstance(){
// 1. 創(chuàng)建工具類
Enhancer enhancer = new Enhancer();
// 2. 設置父類
enhancer.setSuperclass(target.getClass());
// 3. 設置回調函數(shù)
enhancer.setCallback(this);
// 4. 創(chuàng)建子類對象,即代理對象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB代理開始");
Object returnValue = method.invoke(target, args);
System.out.println("CGLIB代理結束");
return returnValue;
}
}
public class Client {
public static void main(String[] args){
// 創(chuàng)建被代理的對象
TeacherDaoImpl target = new TeacherDaoImpl();
// 獲取代理對象,并將被代理對象傳給代理對象
TeacherDaoImpl proxy = (TeacherDaoImpl) new CglibProxyFactory(target).getProxyInstance();
// 執(zhí)行方法,觸發(fā)intecept方法,從而實現(xiàn)執(zhí)行被代理對象的方法
proxy.teach();
}
}