Spring AOP 本质(3)

Spring AOP 本质(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 */ }

    ....
}
 
在开发中自己实现的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的配置才能深谙其中的精妙!
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值