- Public class ProxyViewAction implements Action
- {
- private Action action = new ViewAction();
- public void doAction()
- {
-
- if(Permission.getPermission(userId).equals(Constants.VIEW))
- {
- action.doAction();
- }
- }
- }
Public class ProxyViewAction implements Action{ private Action action = new ViewAction(); public void doAction() { //調(diào)用權(quán)限類的方法取得用戶權(quán)限 if(Permission.getPermission(userId).equals(Constants.VIEW)) { action.doAction();}}}
這是代理類,很容易理解。在我們的ProxyViewAction類中,除了做了客戶真正想要做的動作:doAction()以外,還進行了 額外 的動作檢查用戶的權(quán)限。而作核心動作doAction()是在一個 干干凈凈 的類:ViewAction中進行,這個類只做 核心動作 ,對其他的不關(guān)心,滿足了單一職責(zé)原則。 客戶端通過調(diào)用代理類來執(zhí)行動作 ,而代理類一是將權(quán)限判斷和動作的執(zhí)行分離開來,滿足了單一職責(zé)原則;二是實現(xiàn)了 同 一個接口,從而滿足了依賴顛倒原則。比第一個思路好了很多。 代理又被稱為 委派 ,說的是代理類 并不真正的執(zhí)行那個核心動作 ,而是委派給另外一個類去執(zhí)行,如ProxyView類中,ProxyView類并沒有真正執(zhí)行doAction()方法,而是交給ViewAction類去執(zhí)行。 我們再來看代理類ProxyViewAction,可以看到它不僅依賴于接口Action,而且依賴于具體的實現(xiàn)ViewAction。這樣對我們的系統(tǒng)擴展很不利,比如我們有Add動作、Delete動作、Modify動作等等,我們需要對每一個動作都寫一個代理類,而這些代理類都做同樣的事情,先進行權(quán)限判斷,然后再委派。所以我們需要對這些代理再進行一次抽象, 讓它只依賴接口Action,而不依賴于具體的實現(xiàn) 。 要實現(xiàn)這樣的想法,我們需要將代理類中的具體實現(xiàn)提走,讓代理的使用者在運行期提供具體的實現(xiàn)類,即所謂的 依賴注入 ,如下:
- Public class ProxyAction implements Action
- {
- private Action action;
- public ProxyAction(Action action)
- {
- this.action = action;
- }
- public void doAction()
- {
-
- if(Permission.getPermission(userId).equals(action.getClass().getName()))
- {
- action.doAction();
- }
- }
- }
Public class ProxyAction implements Action{ private Action action; public ProxyAction(Action action) { this.action = action;} public void doAction() { //調(diào)用權(quán)限類的方法取得用戶權(quán)限 if(Permission.getPermission(userId).equals(action.getClass().getName())) { action.doAction();}}}
這樣,我們就將所有實現(xiàn)了Action接口的實現(xiàn)使用一個代理類來代理它們。除了ViewAction類能用,以后擴展的AddAction、 ModifyAction、DeleteAction類等等,都可以使用一個代理類:ProxyAction。 而我們的客戶端類似如下: Action action = ProxyAction(new ViewAction); Action.doAction(); 通過對代理類的 依賴注入 ,我們使得代理類初步有了一定擴展性。但是我們還要看到,這個代理類依賴于某一個確定的接口。這仍然不能滿足我們的實際要求,如我們的系統(tǒng)的權(quán)限控制一般是整個系統(tǒng)級的,這樣系統(tǒng)級的權(quán)限控制,我們很難在整個系統(tǒng)里抽象出一個統(tǒng)一的接口,可能會有多個接口,按照上面的代理模式,我們需要對每一個接口寫一個代理類,同樣,這些類的功能都是一樣的。這顯然不是一個好地解決辦法。 基于上面的原因,我們需要解決一個系統(tǒng)在沒有統(tǒng)一的接口的情況下,對一些零散的對象的某一些動作使用代理模式的問題。JAVA API為我們引入了動態(tài)代理或動態(tài)委派的技術(shù)。 動態(tài)代理的核心是 InvocationHandler接口 ,要使用動態(tài)代理就必須實現(xiàn)該接口。這個接口的委派任務(wù)是在invoke(Object proxy, Method m, Object[] args)方法里面實現(xiàn)的: //invoke是調(diào)用的意思 //在調(diào)用核心功能之前作一些動作 …… //調(diào)用核心功能 m.invoke(obj, args); //在調(diào)用核心功能以后做一些動作 …… 我們可以看到動態(tài)代理其實用的是 反射機制 來調(diào)用核心功能的:m.invoke(obj, args);正是這種反射機制的使用使得我們調(diào)用核心功能更加靈活,而 不用依賴于某一個具體的接口,而是依賴于Object對象 。 下面我們來具體看看動態(tài)代理或動態(tài)委派如何使用:
- public class ProxyAction implements InvocationHandler {
- private Object action;
-
- public ProxyAction(Object action)
- {
- this.action = action;
- }
- public static Object getInstance(Object action)
- {
- return Proxy.newProxyInstance(action.getClass().getClassLoader(),
- action.getClass().getInterfaces(),new ProxyAction(action));
- }
-
- public Object invoke(Object proxy, Method m, Object[] args)
- throws Throwable {
-
- Object result;
-
- try {
-
- System.out.println("before method " + m.getName());
-
- result = m.invoke(action, args);
-
- } catch (InvocationTargetException e) {
-
- throw e.getTargetException();
-
- } catch (Exception e) {
-
- throw new RuntimeException("unexpected invocation exception: "
-
- + e.getMessage());
-
- } finally {
-
- System.out.println("after method " + m.getName());
-
- }
-
- return result;
-
-
- }
-
- }
public class ProxyAction implements InvocationHandler {private Object action;//構(gòu)造方法public ProxyAction(Object action){ this.action = action;}public static Object getInstance(Object action){ return Proxy.newProxyInstance(action.getClass().getClassLoader(),action.getClass().getInterfaces(),new ProxyAction(action));} public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Object result; try { //在委派之前作動作,如權(quán)限判斷等 System.out.println("before method " + m.getName()); //進行委派 result = m.invoke(action, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { //在委派之后做動作 System.out.println("after method " + m.getName()); } return result; } }
這個代理類,首先是實現(xiàn)了InvocationHandler接口;然后在getInstance()方法里得到了代理類的實例;在invoke()方法里實現(xiàn)代理功能,也很簡單。 下面我們來看客戶端: Action action = (Action)ProxyAction.getInstance(new ViewAction()); Action.doAction(); 我們可以看到代理類對接口的依賴也轉(zhuǎn)移到了客戶端上,這樣,代理類不依賴于某個接口。對于同樣的代理類ProxyAction,我們也可以有如下的客戶端調(diào)用: Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl()); Engine.execute(); 只要engineImpl類實現(xiàn)了Engine接口,就可以像上面那樣使用。 現(xiàn)在我們可以看到,動態(tài)代理的確是擁有相當(dāng)?shù)撵`活性。但我們同時也看到了,這個代理類寫起來比較麻煩,而且也差不多每次都寫這樣千篇一律的東西,只有委派前的動作和委派后的動作在不同的代理里有著不同,其他的東西都需要照寫。如果這樣的代理類寫多了,也會有一些冗余代理。需要我們進一步優(yōu)化,這里我們使用模板方法模式來對這個代理類進行優(yōu)化,如下:
- public abstract class BaseProxy implements InvocationHandler {
- private Object obj;
- protected BaseProxy(Object obj)
- {
- this.obj = obj;
- }
- public static Object getInstance(Object obj,InvocationHandler instance)
- {
- return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
- obj.getClass().getInterfaces(),instance);
- }
-
- public Object invoke(Object proxy, Method m, Object[] args)
- throws Throwable {
-
- Object result;
-
- try {
-
- System.out.println("before method " + m.getName());
- this.doBegin();
-
- result = m.invoke(obj, args);
-
- } catch (InvocationTargetException e) {
-
- throw e.getTargetException();
-
- } catch (Exception e) {
-
- throw new RuntimeException("unexpected invocation exception: "
-
- + e.getMessage());
-
- } finally {
-
- System.out.println("after method " + m.getName());
- this.doAfter();
-
- }
-
- return result;
-
-
- }
- public abstract void doBegin();
- public abstract void doAfter();
-
- }
public abstract class BaseProxy implements InvocationHandler {private Object obj;protected BaseProxy(Object obj){ this.obj = obj;}public static Object getInstance(Object obj,InvocationHandler instance){ return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),instance);} public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { // TODO Auto-generated method stub Object result; try { System.out.println("before method " + m.getName()); this.doBegin(); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); this.doAfter(); } return result; }public abstract void doBegin();public abstract void doAfter(); }
這樣,代理的實現(xiàn)類只需要關(guān)注實現(xiàn)委派前的動作和委派后的動作就行,如下
- public class ProxyImpl extends BaseProxy {
- protected ProxyImpl(Object o)
- {
- super(o);
- }
- public static Object getInstance(Object foo)
- {
- return getInstance(foo,new ProxyImpl(foo));
- }
-
-
- public void doBegin() {
-
- System.out.println("begin doing....haha");
-
- }
-
-
- public void doAfter() {
-
- System.out.println("after doing.....yeah");
-
- }
-
- }
public class ProxyImpl extends BaseProxy {protected ProxyImpl(Object o){ super(o);}public static Object getInstance(Object foo){ return getInstance(foo,new ProxyImpl(foo));} //委派前的動作public void doBegin() { // TODO Auto-generated method stub System.out.println("begin doing....haha"); } //委派后的動作public void doAfter() { // TODO Auto-generated method stub System.out.println("after doing.....yeah"); } }
從上面的代碼,我們可以看出代理實現(xiàn)類的確是簡單多了,只關(guān)注了委派前和委派后的動作,這是我們作為一個代理真正需要關(guān)心的。 至此,代理模式和動態(tài)代理已經(jīng)告一段落。我們將動態(tài)代理引申一點說開去,來作為這篇文章的蛇足。 這個話題就是面向方面的編程,或者說AOP。我們看上面的ProxyImpl類,它的兩個方法doBegin()和doAfter(),這是做核心動作之前和之后的兩個截取段。正是這兩個截取段,卻是我們AOP的基礎(chǔ)。在OOP里,doBegin(),核心動作,doAfter()這三個動作在多個類里始終在一起,但他們所要完成的邏輯卻是不同的,如doBegin()可能做的是權(quán)限,在所有的類里它都做權(quán)限;而在每個類里核心動作卻各不相同;doAfter()可能做的是日志,在所有的類里它都做日志。正是因為在所有的類里,doBegin()或doAfter()都做的是同樣的邏輯,因此我們需要將它們提取出來,單獨分析、設(shè)計和編碼,這就是我們的AOP的思想。 這樣說來,我們的動態(tài)代理就能作為實現(xiàn)AOP的基礎(chǔ)了。好了,就說這么多,關(guān)于AOP技術(shù),我們可以去關(guān)注關(guān)于這方面的知識。