參考書目:
《從零開始寫Java Web框架》黃勇
1. 代理模式
代理的思想是使用一個(gè)代理類封裝一個(gè)具有某個(gè)方法的類晕翠,當(dāng)我們需要在外部使用這個(gè)方法的時(shí)候紧卒,我們通過調(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ù)雜守屉,這里就不將所有代碼列出來了。我們只看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)參照源碼
}
接下來我們使用這兩個(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è)類來完成了一個(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來代理單端single-ended(多對(duì)一和一對(duì)一)關(guān)聯(lián)(對(duì)集合的延遲抓取晋南,是采用其他機(jī)制實(shí)現(xiàn)的)惠猿。
通過maven引入最新版的gclib:
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.1</version>
</dependency>
接下來我們使用CGlib提供的相關(guān)API來實(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!");