JDK動態(tài)代理是
java.lang.reflect.*包提供的方式,它必須借助一個借口才能產(chǎn)生一個對象,也就是說JDK動態(tài)代理是對接口的代理。一般要使用JDK動態(tài)代理,首先得定義接口,然后再對這個接口的實現(xiàn)類對象進(jìn)行代理,產(chǎn)生代理對象。Spring的AOP就是JDK動態(tài)代理的應(yīng)用之一。
第一步:定義接口
JDK動態(tài)代理必須有接口,這里僅僅定義一個及其簡單的接口用于示例。
package com.lemon.designmode.interfaces;
/**
* @author lemon
* @date 2018/2/11 上午10:25
*/
public interface HelloWorld {
void sayHello();
}
第二步:實現(xiàn)接口
這里是接口的實現(xiàn)類,具體的業(yè)務(wù)邏輯寫在這里。
package com.lemon.designmode.bean;
import com.lemon.designmode.interfaces.HelloWorld;
/**
* @author lemon
* @date 2018/2/11 上午10:23
*/
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello() {
System.out.println("向Java世界問好...");
}
}
這里是最簡單的Java接口和實現(xiàn)類的關(guān)系,此時可以開始動態(tài)代理了,一般會分為兩個步驟:第一是建立代理對象和真是服務(wù)對象的代理和被代理關(guān)系,第二步是時間代理對象具體方法的邏輯。
第三步:動態(tài)代理綁定和代理邏輯實現(xiàn)
在JDK動態(tài)代理中,要實現(xiàn)代理邏輯類必須娶實現(xiàn)java.lang.reflect.InvocationHandler接口,它內(nèi)部定義了一個invoke方法,并提供接口數(shù)組用于將代理對象下掛到被代理對象所屬的接口下,也就是說代理對象和被代理對象都是實現(xiàn)了同一個接口,只不過被代理對象是顯示實現(xiàn)(使用了關(guān)鍵字implements),代理對象是運(yùn)行時實現(xiàn)。
package com.lemon.designmode.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author lemon
* @date 2018/2/11 上午10:27
*/
public class JdkProxyExample implements InvocationHandler {
private Object target;
/**
* 該方法用于將真實對象綁定到代理類,并返回代理對象
*
* @param target 真實服務(wù)對象
* @return 代理對象
*/
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 代理方法邏輯
*
* @param proxy 代理對象
* @param method 當(dāng)前調(diào)度方法
* @param args 被代理對象的方法需要的參數(shù)
* @return 代理結(jié)果
* @throws Throwable 異常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("進(jìn)入代理方法");
System.out.println("代理方法調(diào)用真實對象之前的邏輯處理");
Object obj = method.invoke(target, args);
System.out.println("代理方法調(diào)用真實對象之后的邏輯處理");
return obj;
}
}
上面的代碼實現(xiàn)了兩步:第一是將真實服務(wù)對象和代理對象進(jìn)行了綁定;第二步是實現(xiàn)了代理邏輯。
這里通過bind方法將真實服務(wù)對象保存到了代理類的成員變量中,并返回了代理對象。
newProxyInstance方法分析:
1)第一個參數(shù)是類加載器,這里使用的是target的類加載器
2)第二個參數(shù)是傳入真實服務(wù)對象所在的接口,用于將代理對象下掛到和真實服務(wù)對象所在的接口
3)第三個參數(shù)是定義實現(xiàn)方法邏輯的代理類,this表示當(dāng)前類對象,也就是代理類,那么這個代理類就必須實現(xiàn)InvocationHandler接口的invoke方法,它就是代理邏輯的實現(xiàn)方法。
invoke方法分析:
1)第一個參數(shù)是代理對象,就是bind方法生成的對象
2)第二個參數(shù)是當(dāng)前調(diào)度的方法
3)第三個參數(shù)是調(diào)度方法的參數(shù)。
第四步:測試JDK動態(tài)代理
package com.lemon.designmode.test;
import com.lemon.designmode.bean.HelloWorldImpl;
import com.lemon.designmode.interfaces.HelloWorld;
import com.lemon.designmode.proxy.JdkProxyExample;
/**
* @author lemon
* @date 2018/2/11 上午10:44
*/
public class JdkProxyTest {
public static void main(String[] args) {
JdkProxyExample jdkProxyExample = new JdkProxyExample();
HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl());
proxy.sayHello();
}
}
這里使用代理對象直接調(diào)用真實服務(wù)對象同名方法,實現(xiàn)了零入侵式代碼增強(qiáng)。Spring AOP就是這個原理,在真實服務(wù)對象方法加入前置通知、后置通知、環(huán)繞通知等。
測試方法運(yùn)行結(jié)果如下:
進(jìn)入代理方法
代理方法調(diào)用真實對象之前的邏輯處理
向Java世界問好...
代理方法調(diào)用真實對象之后的邏輯處理
如果想了解
CGLIB動態(tài)代理,請移步另一篇博客《CGLIB動態(tài)代理》。