前言
Java語言本身是具有動態(tài)性的为障,在我們平時使用中耻蛇,存在兩種動態(tài)代理恶座,分別是出自官方之手的JDK動態(tài)代理鲤拿,一種是出自民間的CGLib(Code Generation Library)胶台。
JDK動態(tài)代理
JDK動態(tài)代理依賴接口歼疮,所以我們首先創(chuàng)建一個接口類 Hello
public interface Hello {
public void sayHello();
}
接下來就是一個接口實(shí)現(xiàn)類 HelloImpl
public class HelloImpl implements Hello {
public void sayHello() {
System.out.println("HelloImpl is invoking sayHello() method...");
}
}
至此,我們準(zhǔn)備工作已經(jīng)做好了诈唬,接下來看看我們還需要做哪些工作
我們需要創(chuàng)建一個類韩脏,這個類的作用是給我們動態(tài)創(chuàng)建出來的代理類調(diào)用的,暫且命名為 DynamicProxy 吧铸磅。該類需要繼承自 InvocationHandler 接口赡矢,并實(shí)現(xiàn) invoke() 方法。
public class DynamicProxy implements InvocationHandler {
//委托對象實(shí)例
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("sayHello")) {
before();
Object result = method.invoke(target,args);
after();
return result;
}
return null;
}
public void before() {
System.out.println("before invoke sayHello() method");
}
public void after() {
System.out.println("after invoke sayHello() method");
}
}
可以看到在 invoke() 方法中我們在方法執(zhí)行前后添加了自定義的行為阅仔,我們稱之為方法攔截吹散。接下來就是書寫 main() 方法了八酒。
public class ProxyTest {
public static void main(String[] args) {
//創(chuàng)建實(shí)現(xiàn)接口的對象
Hello helloImpl = new HelloImpl();
//該類的作用就是對方法進(jìn)行攔截
DynamicProxy dynamicProxy = new DynamicProxy(helloImpl);
//在運(yùn)行過程中動態(tài)創(chuàng)建代理類
Hello helloProxy = (Hello) Proxy.newProxyInstance(helloImpl.getClass().getClassLoader(),
helloImpl.getClass().getInterfaces(),
dynamicProxy);
//通過代理類調(diào)用方法
helloProxy.sayHello();
}
}
具體的各類之間關(guān)系我們還是來看UML圖吧空民。
如上圖,我們可以看到羞迷,通過JDK生成的動態(tài)代理類$HelloProxy100與委托類(實(shí)現(xiàn)Hello接口的HelloImpl類)是兄弟關(guān)系界轩,當(dāng)要執(zhí)行動態(tài)代理類$HelloProxy100中的任一方法時画饥,都會去調(diào)用DynamicProxy類中的invoke()方法,這樣我們就對方法實(shí)現(xiàn)了攔截浊猾。
JDK動態(tài)代理優(yōu)化
我們在 DynamicProxy 類中添加一個獲取代理類的方法荒澡,以后每次通過這個 DynamicProxy 中間類來獲取代理類。
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
this.target.getClass().getInterfaces(),
this);
}
這樣我們的 main() 方法中的代碼量就大大減少了与殃。
public class ProxyTest {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello helloProxy = dynamicProxy.getProxy();
helloProxy.sayHello();
}
}
CGLib動態(tài)代理
與JDK動態(tài)代理相比单山,CGLib動態(tài)代理是不需要接口的,這里就創(chuàng)建一個普通類 Hi
public class Hi {
public void sayHi() {
System.out.println("Hi,my friend");
}
}
接著幅疼,創(chuàng)建出一個與 DynamicProxy 作用(給動態(tài)創(chuàng)建出的代理類調(diào)用)相似的類米奸,創(chuàng)建一個叫做 CGLibProxy 的類,該類必須實(shí)現(xiàn) MethodInterceptor 接口爽篷,并實(shí)現(xiàn) interceptor() 方法悴晰。
public class CGLibProxy implements MethodInterceptor {
public <T> T getProxy(Class<?> cls) {
return (T) Enhancer.create(cls,this);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
//執(zhí)行委托類中的原方法
Object result = methodProxy.invokeSuper(o,objects);
after();
return result;
}
public void before() {
System.out.println("before method");
}
public void after() {
System.out.println("after method");
}
}
最后書寫 main() 方法。
public class ProxyTest {
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
Hi hiProxy = cgLibProxy.getProxy(Hi.class);
hiProxy.sayHi();
}
}
各類關(guān)系我們還是使用UML來表示
可以看出逐工,生成的動態(tài)代理類 $HiProxy100 是委托類的子類铡溪,通過反編譯工具查看 $HiProxy100 源代碼可得知,該類重寫了委托類的 sayHi() 方法泪喊,如果發(fā)現(xiàn)存在實(shí)現(xiàn)了 MethodInterceptor 接口的對象棕硫,那么就會通過這個對象調(diào)用 intercept() 方法對委托類中的每一個方法進(jìn)行攔截(除了final方法,因?yàn)閒inal方法不能被繼承袒啼,所以動態(tài)代理類自然就沒有這個方法)哈扮。