Spring AOP 本質(3)

Spring AOP 本質(3)
 
Spring AOP很牛,AOP是OOP的補充,而非競爭者。
前面的例子離實際的應用太遙遠。不足以顯式AOP的力量,如今就用AOP前置通知來檢查用戶的身份,只有經過檢查的才能調用業務方法。
 
在沒有使用AOP以前,咱們是如何實現的?想一想看。
 
一、寫一個安全檢查類,又其餘類繼承,並在子類的業務方法中調用安全檢查的方法。
好比:Struts1時代就是繼承Action,其類結構以下:
package org.apache.struts.action;

public class Action {

     public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, ServletRequest servletRequest, ServletResponse servletResponse) throws java.lang.Exception { /* compiled code */ }

     public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, http.HttpServletRequest httpServletRequest, http.HttpServletResponse httpServletResponse) throws java.lang.Exception { /* compiled code */ }

    ....
}
 
在開發中本身實現的UserAction須要繼承Struts的Action,能夠考慮作一個抽象,好比叫作CheckAciton,在其中重寫execute方法,並加入安全檢查機制,而且增長一個抽象請求處理方法
public ActionForward real(ActionMapping actionMapping, ActionForm actionForm, http.HttpServletRequest httpServletRequest, http.HttpServletResponse httpServletResponse)throws java.lang.Exception;
做爲業務請求處理的方法,放到重寫的execute方法內部調用。
 
public class CheckAction extends Action{

     public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, http.HttpServletRequest httpServletRequest, http.HttpServletResponse httpServletResponse) throws java.lang.Exception {
     //todo: 安全檢查邏輯
     return real(actionMapping,actionForm,httpServletRequest,httpServletResponse);

    }

     public abstract ActionForward real(ActionMapping actionMapping, ActionForm actionForm, http.HttpServletRequest httpServletRequest, http.HttpServletResponse httpServletResponse) throws java.lang.Exception;

}
 
這樣之後業務上的別的Aciton只要繼承了CheckAction,還須要實現real方法,別的方法),便可爲該Action加入安全檢查的邏輯。
 
public class DoSomethingAction extends CheckAction{

     public abstract ActionForward real(ActionMapping actionMapping, ActionForm actionForm, http.HttpServletRequest httpServletRequest, http.HttpServletResponse httpServletResponse) throws java.lang.Exception{
     //todo: 單純處理實際的業務請求
     return ...
    }

    ....
}
 
這樣作也很麻煩,還可使用動態代理爲每一個業務接口加上安全檢查的邏輯,可是性能更差,更麻煩。
 
這個還算可行的方案,實現也很容易。可是很死板,若是有多種驗證策略就比較棘手了。
 
沒有對比就顯式不出來Spring AOP的優點。下面看看Spring的優雅處理:
 
/**
* 用戶登陸信息載體
*/

public class UserInfo {
     private String userName;

     private String password;

     public UserInfo(String userName, String password) {
         this.userName = userName;
         this.password = password;
    }
    
     public String getPassword() {
         return password;
    }
     public String getUserName() {
         return userName;
    }
}
 
/**
* 業務組件:被代理的對象
*/

public class SecureBean {

     /**
     * 示範性的業務方法,這個方法將被攔截,加入一些附加邏輯
     */

     public void businessOperate() {
        System.out.println( "業務方法businessOperate()被調用了!");
    }
}
 
/**
* 安全管理類:檢查用戶登陸和管理用戶註銷登陸的業務邏輯。
*/

public class SecurityManager {
     //爲每個SecurityManager建立一個本地線程變量threadLocal,用來保存用戶登陸信息UserInfo
     private static ThreadLocal threadLocal = new ThreadLocal();

     /**
     * 用戶登陸方法,容許任何用戶登陸。
     * @param userName
     * @param password
     */

     public void login(String userName, String password) {
         // 假定任何的用戶名和密碼均可以登陸
         // 將用戶登陸信息封裝爲UerInfo對象,保存在ThreadLocal類的對象threadLocal裏面
        threadLocal.set( new UserInfo(userName, password));
    }

     public void logout() {
         // 設置threadLocal對象爲null
        threadLocal.set( null);
         int x = 0;
    }

     public UserInfo getLoggedOnUser() {
         // 從本地線程變量中獲取用戶信息UerInfo對象
         return (UserInfo) threadLocal.get();
    }
}
 
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

/**
* 前置通知類
*/

public class SecurityAdvice implements MethodBeforeAdvice {

     private SecurityManager securityManager;

     public SecurityAdvice() {
         this.securityManager = new SecurityManager();
    }

     /**
     * 前置通知的接口方法實現。僅容許robh用戶登陸,強制設定的。
     */

     public void before(Method method, Object[] args, Object target)
             throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();

         if (user == null) {
            System.out.println( "沒有用戶憑證信息!,本前置通知僅僅容許robh用戶登陸,不信你試試看!");
             throw new SecurityException(
                     "你必須在調用此方法" + method.getName() + "前進行登陸:");
        } else if ( "robh".equals(user.getUserName())) {
            System.out.println( "用戶robh成功登陸:OK!");
        } else {
            System.out.println( "非法用戶"+user.getUserName()+ ",請使用robh登陸,用戶調用的方法是:" + method.getName());
             throw new SecurityException( "用戶" + user.getUserName()
                    + " 不容許調用" + method.getName() + "方法!");
        }
    }
}

 
import org.springframework.aop.framework.ProxyFactory;

/**
* 測試類,客戶端
*/

public class SecurityExample {

     public static void main(String[] args) {
         //獲得一個 security manager
        SecurityManager mgr = new SecurityManager();

         //獲取一個SecureBean的代理對象
        SecureBean bean = getSecureBean();

         //嘗試用robh登陸
        mgr.login( "robh", "pwd");   //檢查登陸狀況
        bean.businessOperate();     //業務方法調用
        mgr.logout();               //註銷登陸

         //嘗試用janm登陸
         try {
            mgr.login( "janm", "pwd");       //檢查登陸狀況
            bean.businessOperate();         //業務方法調用
        } catch (SecurityException ex) {
            System.out.println( "發生了異常: " + ex.getMessage());
        } finally {
            mgr.logout();                   //註銷登陸
        }

         // 嘗試不使用任何用戶名身份調用業務方法
         try {
            bean.businessOperate();         //業務方法調用
        } catch (SecurityException ex) {
            System.out.println( "發生了異常: " + ex.getMessage());
        }

    }

     /**
     * 獲取SecureBean的代理對象
     *
     * @return SecureBean的代理
     */

     private static SecureBean getSecureBean() {
         //建立一個目標對象
        SecureBean target = new SecureBean();

         //建立一個通知
        SecurityAdvice advice = new SecurityAdvice();

         //獲取代理對象
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(advice);
        SecureBean proxy = (SecureBean) factory.getProxy();

         return proxy;
    }
}
 
運行結果:
- Using JDK 1.4 collections
用戶robh成功登陸:OK!
業務方法businessOperate()被調用了!
非法用戶janm,請使用robh登陸,用戶調用的方法是:businessOperate
發生了異常: 用戶janm 不容許調用businessOperate方法!
沒有用戶憑證信息!,本前置通知僅僅容許robh用戶登陸,不信你試試看!
發生了異常: 你必須在調用此方法businessOperate前進行登陸:

Process finished with exit code 0
 
觀察運行結果,精確實現了驗證的要求。
 
這裏從底層觀察Spring AOP的應用,實際應用中最好仍是經過xml配置耦合代碼。只有明白了AOP其中奧祕,使用Spring的配置才能深諳其中的精妙!
相關文章
相關標籤/搜索