代理模式
簡介
代理模式是一種常用的設計模式,在AOP、RPC等諸多框架中均有它的身影
- 代理物件存在的價值主要用於攔截對真實業務物件的訪問;
- 代理物件具有和目標物件(真實業務物件)實現共同的介面或繼承於同一個類;
- 代理物件是對目標物件的增強,以便對訊息進行預處理和後處理。
定義與結構
定義 為其他物件提供一種代理以控制對這個物件的訪問。
代理模式主要包含三個角色,即抽象主題角色(Subject)、委託類角色(被代理角色,Proxied)以及代理類角色(Proxy),如上圖所示
- 抽象主題角色:可以是介面,也可以是抽象類;
- 委託類角色:真實主題角色,業務邏輯的具體執行者;
- 代理類角色:內部含有對真實物件RealSubject的引用,負責對真實主題角色的呼叫,並在真實主題角色處理前後做預處理和後處理。
靜態代理實現
靜態代理三步走
1. 定義業務介面
public interface HelloService {
String hello(String name);
String hi(String msg);
}
複製程式碼
2.實現業務介面
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return "Hello " + name;
}
@Override
public String hi(String msg) {
return "Hi, " + msg;
}
}
複製程式碼
3.理類並實現業務介面
public class HelloServiceProxy implements HelloService {
private HelloService helloService;
public HelloServiceProxy(HelloService helloService) {
this.helloService = helloService;
}
@Override
public String hello(String name) {
System.out.println("預處理...");
String result = helloService.hello(name);
System.out.println(result);
System.out.println("後處理...");
return result;
}
@Override
public String hi(String msg) {
System.out.println("預處理...");
String result = helloService.hi(msg);
System.out.println(result);
System.out.println("後處理...");
return result;
}
}
複製程式碼
最後便可通過客戶端進行呼叫
public class Main {
public static void main(String[] args){
HelloService helloService = new HelloServiceImpl();
HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);
helloServiceProxy.hello("Panda");
helloServiceProxy.hi("Panda");
}
}
複製程式碼
JDK 動態代理
動態代理可以很方便地對委託類的相關方法進行統一增強處理,如新增方法呼叫次數、新增日誌功能等等。動態代理主要分為JDK動態代理和cglib動態代理兩大類,本文主要對JDK動態代理進行探討
JDK動態代理使用步驟
- 建立被代理的介面和類
// 抽象主題角色
public interface HelloService {
String hello(String name);
String hi(String msg);
}
// 具體(真實)主題角色
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return "Hello " + name;
}
@Override
public String hi(String msg) {
return "Hi, " + msg;
}
}
複製程式碼
- 實現InvocationHandler介面
public class MyInvocationHandler implements InvocationHandler{
// 真實業務物件
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增強邏輯
System.out.println("PROXY : " + proxy.getClass().getName());
// 反射呼叫,目標方法
Object result = method.invoke(target, args);
// 增強邏輯
System.out.println(method.getName() + " : " + result);
return result;
}
}
複製程式碼
- 建立代理類並生成相應的代理物件
// 生成代理類的class物件
Class<?> clazz = Proxy.getProxyClass(helloService.getClass().getClassLoader(), helloService
.getClass().getInterfaces());
// 建立InvocationHandler
InvocationHandler myInvocationHandler = new MyInvocationHandler(helloService);
// 獲取代理類的構造器物件
Constructor constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
// 反射建立代理物件
HelloService proxy = (HelloService)constructor.newInstance(myInvocationHandler);
複製程式碼
- 使用代理
proxy.hello("rico");
proxy.hi("panda");
複製程式碼
個人看點:
- 實現動態代理的關鍵技術是反射;
- 代理物件是對目標物件的增強,以便對訊息進行預處理和後處理;
- InvocationHandler中的invoke()方法是代理類完整邏輯的集中體現,包括要切入的增強邏輯和進行反射執行的真實業務邏輯;
- 使用JDK動態代理機制為某一真實業務物件生成代理,只需要指定目標介面、目標介面的類載入器以及具體的InvocationHandler即可。
- JDK動態代理的典型應用包括但不僅限於AOP、RPC、Struts2、Spring等重要經典框架。