Java 動(dòng)態(tài)代理和CGlib

參考書目:
《從零開始寫Java Web框架》黃勇

1. 代理模式

代理的思想是使用一個(gè)代理類封裝一個(gè)具有某個(gè)方法的類,當(dāng)我們需要在外部使用這個(gè)方法的時(shí)候,我們通過(guò)調(diào)用代理類實(shí)現(xiàn)。這樣,我們可以在代理類里面定義一些我們想要在這個(gè)方法前面或后面做的事。下面是一個(gè)簡(jiǎn)單的例子:
Java
//定義接口
public interface Hello{
void say(String name);
}

//定義實(shí)現(xiàn)類
public class HelloImpl implements Hello{
public void say(String name){
System.out.println("hello , " +name);
}
}

//定義代理
public class HelloProxy implements Hello{
private Hello hello;

public HelloProxy(){
this.hello = new HelloImpl();
}

public void say(String name){
before();
hello.say(name);
after();
}

public void before(){
//do something
}

public void after(){
//do something
}
}

//使用
Hello helloProxy = new HelloProxy();
helloProxy.say("captain !")

### 2.動(dòng)態(tài)代理
很容易看出,我們?cè)谏弦粋€(gè)部分中實(shí)現(xiàn)的 HelloProxy類只能對(duì)HelloImpl進(jìn)行代理,而當(dāng)我們有多個(gè)接口和實(shí)現(xiàn)類需要代理的時(shí)候,需要對(duì)每個(gè)接口的實(shí)現(xiàn)編寫一個(gè)代理類。這顯然是不夠優(yōu)雅的做法。更好的解決方法是使用“動(dòng)態(tài)代理”。所謂動(dòng)態(tài)代理,也就是可以動(dòng)態(tài)設(shè)置它所代理的類的代理。JDK本身為我們實(shí)現(xiàn)動(dòng)態(tài)代理提供了兩個(gè)類:InvocationHandler和Proxy。InvocationHandler源碼如下:
```Java```
//InvocationHandler 源碼
package java.lang.reflect;
public interface InvocationHandler {
  //方法調(diào)用函數(shù)
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

Proxy源碼較復(fù)雜,這里就不將所有代碼列出來(lái)了。我們只看Proxy類的一個(gè)方法,具體實(shí)現(xiàn)先不管了。
Java
//根據(jù)一個(gè)類的ClassLoader, 該類實(shí)現(xiàn)的所有接口和一個(gè)InvocationHandler生成一個(gè)代理該類的對(duì)象(這個(gè)對(duì)象實(shí)現(xiàn)了該類所有的接口)
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
... //具體實(shí)現(xiàn)參照源碼
}

接下來(lái)我們使用這兩個(gè)類重寫Hello的代理:
```Java```
public class MyHandler implements InvocationHandler{
  private Object target;
  public MyHandler(Object target){
    this.target = target;
  }
  @override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
    before();
    Object result = method.invoke(target, args);
    after();
    return result;
  }
  ...
}
// 使用
Hello hello = new HelloImpl();
MyHandler handler = new MyHandler(hello);
Hello helloProxy = (Hello)Proxy.newProxyInstance(hello.getClass().getClassLoader(),hello.getClass().getInterfaces(),handler);
helloProxy.say("Captain!");

3.CGlib動(dòng)態(tài)代理

在上一部分,我們使用JDK內(nèi)置的兩個(gè)類來(lái)完成了一個(gè)動(dòng)態(tài)代理的實(shí)現(xiàn),但是這個(gè)實(shí)現(xiàn)仍然有很嚴(yán)重的限制,那就是它只能為實(shí)現(xiàn)了接口的類進(jìn)行代理。假如我們要代理任意的類,更好的選擇是使用CGLib庫(kù)。CGLIB是一個(gè)強(qiáng)大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。最流行的OR Mapping工具h(yuǎn)ibernate也使用CGLIB來(lái)代理單端single-ended(多對(duì)一和一對(duì)一)關(guān)聯(lián)(對(duì)集合的延遲抓取,是采用其他機(jī)制實(shí)現(xiàn)的)。
通過(guò)maven引入最新版的gclib:
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.1</version>
</dependency>

接下來(lái)我們使用CGlib提供的相關(guān)API來(lái)實(shí)現(xiàn)Hello的代理:
```Java```
public class CGLibProxy implements MethodInterceptor{
  public <T> T getProxy(Class<T> cls){
    return (T)Enhancer.create(cls,this);
  }
  
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable{
    before();
    Object result = proxy.invokeSuper(obj, args);
    after();
    return result;
  }
  ...
}

//使用
CGLibProxy cgLibProxy = new CGLibProxy();
Hello helloProxy = cgLibProxy.getProxy(HelloImpl.class);
helloProxy.say("Captain!");
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容