SpringAOP本質(3)
Spring AOP很牛,AOP是OOP的補充,而非競爭者。
前面的例子離實際的應用太遙遠。不足以顯式AOP的力量,現在就用AOP前置通知來檢查使用者的身份,只有通過檢查的才能呼叫業務方法。
在沒有使用AOP之前,我們是如何實現的?想想看。
1、寫一個安全檢查類,又其他類繼承,並在子類的業務方法中呼叫安全檢查的方法。
比如: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 */ }
….
}
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 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;
}
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 …
}
….
}
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 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 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();
}
}
* 安全管理類:檢查使用者登入和管理使用者登出登入的業務邏輯。
*/
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.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;
}
}
/**
* 測試類,客戶端
*/
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
使用者robh成功登入:OK!
業務方法businessOperate()被呼叫了!
非法使用者janm,請使用robh登入,使用者呼叫的方法是:businessOperate
發生了異常: 使用者janm 不允許呼叫businessOperate方法!
沒有使用者憑證資訊!,本前置通知僅僅允許robh使用者登入,不信你試試看!
發生了異常: 你必須在呼叫此方法businessOperate前進行登入:
Process finished with exit code 0
觀察執行結果,精確實現了驗證的要求。
這裡從底層觀察Spring AOP的應用,實際應用中最好還是通過xml配置耦合程式碼。只有明白了AOP其中奧祕,使用Spring的配置才能深諳其中的精妙!
本文轉自 leizhimin 51CTO部落格,原文連結:http://blog.51cto.com/lavasoft/75291,如需轉載請自行聯絡原作者
相關文章
- vue3 不支援 IE11的本質VueIE11
- 遊戲設計的本質(一):數值的本質遊戲設計
- IT安全的本質
- Lisp 的本質Lisp
- springAOPSpring
- Category的本質<一>Go
- Battle Pass的本質BAT
- OC物件的本質物件
- OC指標的本質指標
- 學習的本質
- 聊聊 ChatGPT 的本質ChatGPT
- springAop原理Spring
- SpringAOP分析Spring
- 透過WebGL 3D看動畫Easing函式本質Web3D動畫函式
- 從缺陷率到質效工作的本質
- 幾何本質初步猜想
- 矩陣合同的本質矩陣
- react之JSX本質ReactJS
- 微信小程式的本質微信小程式
- 加密貨幣的本質加密
- ASP.NET本質論ASP.NET
- 程式設計的本質程式設計
- 人生規劃的本質
- SpringAOP的使用Spring
- 透過HT for Web 3D看動畫Easing函式本質Web3D動畫函式
- Objective-C 類的本質Object
- 架構設計的本質架構
- Block學習①--block的本質BloC
- 程式設計師的本質程式設計師
- 技術的本質與啟示
- 聊聊C語言和指標的本質C語言指標
- 透過三消看遊戲本質遊戲
- Go slice切片的“陷阱”和本質Go
- iOS-block本質是什麼?iOSBloC
- OC原始碼剖析物件的本質原始碼物件
- Category的本質<三>關聯物件Go物件
- 反向代理的本質是什麼?
- 深入理解原子操作的本質