深入理解靜態代理與JDK動態代理

java閘瓦發表於2019-04-11

代理模式

深入理解靜態代理與JDK動態代理

簡介

代理模式是一種常用的設計模式,在AOP、RPC等諸多框架中均有它的身影

  • 代理物件存在的價值主要用於攔截對真實業務物件的訪問;
  • 代理物件具有和目標物件(真實業務物件)實現共同的介面或繼承於同一個類;
  • 代理物件是對目標物件的增強,以便對訊息進行預處理和後處理。

定義與結構

定義 為其他物件提供一種代理以控制對這個物件的訪問。

深入理解靜態代理與JDK動態代理

代理模式主要包含三個角色,即抽象主題角色(Subject)、委託類角色(被代理角色,Proxied)以及代理類角色(Proxy),如上圖所示

  • 抽象主題角色:可以是介面,也可以是抽象類;
  • 委託類角色:真實主題角色,業務邏輯的具體執行者;
  • 代理類角色:內部含有對真實物件RealSubject的引用,負責對真實主題角色的呼叫,並在真實主題角色處理前後做預處理和後處理。

靜態代理實現

深入理解靜態代理與JDK動態代理

靜態代理三步走

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動態代理

動態代理可以很方便地對委託類的相關方法進行統一增強處理,如新增方法呼叫次數、新增日誌功能等等。動態代理主要分為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");
複製程式碼

個人看點:

  1. 實現動態代理的關鍵技術是反射;
  2. 代理物件是對目標物件的增強,以便對訊息進行預處理和後處理;
  3. InvocationHandler中的invoke()方法是代理類完整邏輯的集中體現,包括要切入的增強邏輯和進行反射執行的真實業務邏輯;
  4. 使用JDK動態代理機制為某一真實業務物件生成代理,只需要指定目標介面、目標介面的類載入器以及具體的InvocationHandler即可。
  5. JDK動態代理的典型應用包括但不僅限於AOP、RPC、Struts2、Spring等重要經典框架。

相關文章