由於動態代理之前介紹過的(JDK動態代理、CGLIB動態代理)在實際開發一般比較難理解,程序設計者會設計一個攔截器接口供考法這使用,開發者只需知道攔截器接口的方法,含義和作用即可,無需 知道動態代理是怎麼實現的,用JDK動態代理來實現一個攔截器的邏輯,爲此先定義攔截器接口,代碼如下:
定義攔截器接口
/**
* @author AmVilCres
* <p>
* 該接口定義了3個方法:before、around、after
* <li>3個方法的參數爲:proxy代理對象、target真實的對象、method方法、args運行方法參數</li>
* <li>before方法返回boolean值,它在真實對象前調用。當返回true時,則反射真實對象的方法;當返回false時,則調用around方法</li>
* <li>在反射真實對象方法或者around方法執行之後,調用after方法</li>
* </p>
*/
public interface Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args);
public void around(Object proxy, Object target, Method method, Object[] args);
public void after(Object proxy, Object target, Method method, Object[] args);
}
接口的上面註釋已經寫得很明白了,相信大家都能看懂,接下來寫一個接口的實現類:
public class MyInterceptor implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.err.println("反射方法前邏輯");
return false; //不反射被代理對象的原有方法
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理對象的方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法後邏輯");
}
}
使用JDK動態代理,就可以去實現這些方法在適當時調用的邏輯了,JDK動態代理中使用攔截器代碼如下:
public class InterceptorJdkProxy implements InvocationHandler{
private Object target; //真實對象
private String interceptorClass; // 攔截器全限定名
public InterceptorJdkProxy(Object target,String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
* 綁定委託對象並返回一個 【代理佔位】
*
* @param target 真實的對象
* @return 代理對象【佔位】
* */
public static Object bind(Object target, String interceptorClass) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass));
}
/**
* 通過代理對象調用方法,首先進入的是這個方法
*
* @param proxy 代理對象
* @param method 被調用的方法
* @param args 方法的參數
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(interceptorClass == null)
return method.invoke(target, args);
Object res = null;
//通過反射生成攔截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
if(interceptor.before(proxy, target, method, args))
res = method.invoke(target, args); //反射原有對象方法
else {
interceptor.around(proxy, target, method, args);
}
interceptor.after(proxy, target, method, args);
return res;
}
}
代碼說明:這裏有兩個屬性,一個是target,它是真實的對象;另一個是字符串interceptorClass,它是一個類的全限定名
執行步驟:
1. 在bind方法中用JDK動態代理綁定一個對象,然後返回代理對象
2. 如果沒有設置攔截器,則直接反射真實對象的方法,然後結束,否則進行第3步,
3. 通過反射生成攔截器,並準備使用它
4. 調用攔截器的before方法,如果返回true,反射原來的方法,否則運行攔截器的around方法
5. 調用攔截的after方法
6. 返回結果
攔截器工作流程圖:
測試代碼:
public class TestInterceptorJdkProxy {
public static void main(String[] args) {
// "com.avc.interceptor.MyInterceptor"我定義的爲攔截器的全限定名
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.avc.interceptor.MyInterceptor");
proxy.sayHello();
}
}
運行結果:
責任鏈模式
上面討論了設計者可能會用攔截器去代替動態代理,然後將接口提供給開發者,從而簡化開發者難度,但是攔截器可能有多個。舉個栗子, 某人需要請假一週,如果把請假申請單看成一個對象,那麼他需要經過項目經理、部門經理、人事等多個角色的審批,每個角色都有機會通過攔截這個申請進行審批或者修改。這個時候就要考慮提供這3個角色的處理邏輯,所以需要提供3個攔截器,而傳遞的則是請假申請單 如圖:
當一個對象在一條鏈被多個攔截器攔截處理(攔截器也可以選擇不攔截他)時,就把這樣的設計模式稱爲責任鏈模式,它用於一個對象在多個角色中傳遞的場景。還是剛纔的例子,申請單走到項目經理那,可能吧時間改爲5天,從而影響了後面的審批,後面的審批都要根據前面的結果進行。這個時候考慮用層層代理來實現,就是當申請單(target)走到項目經理處,使用第一個動態代理proxy1,走到部門經理處,部門經理會得到一個在項目經理的代理proxy1基礎上生成的proxy2,當走到人事的時候,會在proxy2的及出生成proxy3,如果還有其他角色,以此類推,下圖描述會更清晰:
定義3個攔截器(實現上面定義的接口)
攔截器1:
public class Interceptor1 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 1 的 before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 1 的 around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 1 的 after方法");
}
}
----------------------------------
攔截器2:
public class Interceptor2 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 2 的 before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 2 的 around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 2 的 after方法");
}
}
---------------------------------------
攔截器3:
public class Interceptor3 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 3 的 before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 3 的 around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("攔截器 3 的 after方法");
}
}
測試類:
public class LinkInterceptorTest {
public static void main(String[] args) {
HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.avc.interceptor.link.Interceptor1");
HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(proxy1, "com.avc.interceptor.link.Interceptor2");
HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(proxy2, "com.avc.interceptor.link.Interceptor3");
proxy3.sayHello();
}
}
運行結果:
可以通過改變before方法的返回值,從而得到不同的結果!
代碼下載:上述代碼的源碼,導入eclipse即可運行,有問題請留言