關於在SpringMVC框架中 實現數據庫session會話環境變量的功能

前言:
由於SpringMVC框架對於數據庫會話處理方面,實現了會話池的功能,以提高數據庫的工作效率。因爲在數據庫申請一個新的會話session確實是比較耗資源。
問題:
但是,這樣子,session被公用就帶來一個新的問題:數據庫的session級的環境變量就可能不可以直接使用了。
首先我們要理解,爲什麼要用數據庫的session的環境變量?用過OracleEBS開發的人都知道,數據庫的session環境變量是相互隔離的:不同的session,session級的環境變量不同。舉個例子,用戶A登錄用一個session,可以在session級添加一個環境變量:用戶是A。然後,這個會話的所有的DML修改,獲取新增人或者更新用戶的時候,都可以自動抓到該session的環境變量即可。
可以大大提高開發的工作效率。
但是,由於SpringMVC框架的原因,在jdbc調用數據庫的時候,可能session被公用。這樣子就很可能出現張冠李戴的問題了。
問題解決:
這個問題也困擾了一點時間。
最後我的解決辦法是:
在Service層添加一個AOP初始化對應的會話的環境變量。切面所有的Service的代碼,在代碼執行之前,先重新初始化該session的環境變量,即可。
需要注意的是,這樣子做必須要注意2個問題:
1 Service層必須是啓用Transaction事務處理的。因爲啓用了事務處理,service在調用數據庫session的時候,纔可以線程安全。經過測試,如果一個事務沒執行完畢;另外一個新的sql要做事務的時候,是自動調用一個新的數據庫session的!
2 需要注意SpringMVC框架的單例模式而帶來的安全性的問題。
最簡單初始化環境變量的ID是通過參數,從controller層傳到Service層。這樣子就沒風險了。然後AOP可以直接監控到對應的ID,再自動初始化環境變量。但是,這個辦法有一個致命的缺點:就是必須爲每個方法添加參數。對於大型系統來說簡直是噩夢,要改N個功能。
所以,我用的是另外一個辦法:用java.lang.ThreadLocal技術提供線程安全。然後,在AOP運行的時候,可以自動獲取到對應的初始化環境變量的ID信息,進行初始化。
具體步驟:
在Controller層,抓取session對應的環境變量ID,對service層進行初始化:

UVS.setLoginId((Long)sess.getAttribute("LOGIN_ID"));

對應的,在Service層添加ThreadLocal技術的成員變量:

    private ThreadLocal<Long> loginIdTL = new ThreadLocal<Long>();

    public Long getLoginId() {
        return this.loginIdTL.get();
    }

    public void setLoginId(Long loginId) {
        this.loginIdTL.set(loginId); 
    }

最後,寫一個AOP,切所有的service層的代碼,進行環境變量的自動初始化:

    /*** 
     * service層調用之前先自動初始化環境變量
     * @throws Exception 
     */  
    @Before("execution(* com.xinyiglass.springSample.service..*.*(..))")  
    public void alb2bInit(JoinPoint joinPoint) throws Exception{
        Method method = null;
        Object target = null;
        Long loginId=null;
        String methodName = "getLoginId";
        target = joinPoint.getTarget();
        method = getMethodByClassAndName(target.getClass(), methodName);
        if(method!=null){
            loginId=(Long) method.invoke(target);//LogUtil.log("loginId:"+loginId);
            if(loginId!=null&&loginId>0&&loginId!=utilDao.getLoginId()){
                PlsqlRetValue ret =utilDao.alb2bInit(loginId);//初始化環境變量!
                if(ret!=null&&!TypeConvert.isNullValue(ret.getErrbuf())) LogUtil.log("ret:"+ret.toJsonStr());
            }
        }
    }    

完工!

後語:
必須要理解幾個知識點。
特別是關於Spring單例模式導致其層的全局變量的問題:
http://blog.sina.com.cn/s/blog_12bf601660102uwq2.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章