2013-08-29 25 views
1

我正在使用存儲String的ThreadLocal對象。我將字符串值設置爲篩選器中的ThreadLocal對象,該對象攔截受某些條件限制的所有請求。此外,我將ThreadLocal的字符串值作爲屬性設置爲HttpSession。使用ThreadLocal與Filter,HttpSession和會話屬性時的不一致

會話中的屬性用於多個jsps,並最終傳遞到業務層。

我面臨的問題是,來自不同客戶端的多個請求在某個時間點會得到相同的字符串值,儘管會話不同。

所以我的理解是,多個會話訪問相同的線程。我沒有看到任何其他解釋。

將屬性設置爲請求會導致在jsps之間移動時出現問題。由於彈簧安全性存在重定向,這意味着請求屬性丟失。

那麼有什麼辦法可以改變實現,使多個會話不使用相同的線程?

編輯:添加代碼示例

public class ContextFilter implements Filter { 

    //No significant variables other than constants 

    public void doFilter(ServletRequest request, ServletResponse response, 
       FilterChain chain) throws IOException, ServletException { 

      // Set the Session Timeout Object 
      SessionTimeoutObject obj = (SessionTimeoutObject) httpRequest 
        .getSession().getAttribute(KEY); 
      if (obj == null) { 
       httpRequest.getSession().setAttribute(KEY, 
         new SessionTimeoutObject()); 
      } 

      if(some conditions) { 
       chain.doFilter(request, response); 
      } else { 
       //Defaulting identifier 
       String identifier = "init"; 

       if (ContextHolder.getId() != null 
         && !ContextHolder.getId().equals("")) { 
        identifier = ContextHolder.getId()); 
       } 

       //Do some thing 

       //Compare the identifier with the value in session and use if it exists 
       String existingId = (String) httpRequest.getSession() 
         .getAttribute(ID_KEY); 
       if (existingId != null 
         && !existingId.trim().equals("")) { 
        identifier = existingId; 
       } 

       //Setting id to context only happens here 
       ContextHolder.setId(identifier); 
       //Validate the identifier 

       //Get Business Obj method using identifier 
       BusinessObj bo = getBusObj(identifier); 

       //everything above is successful so set in session 
       httpRequest.getSession().setAttribute("identifier", identifier); 
       httpRequest.getSession().setAttribute("BusinessObj", 
        bo); 

       //no exceptions yet then good to go 
       chain.doFilter(request, response); 
      } 

    } 


} 

public class SessionTimeoutObject implements Serializable, HttpSessionBindingListener { 

    private String backup; 

    @Override 
    public void valueBound(HttpSessionBindingEvent event) { 
     //Mainly for debuggin what happens to the session 
     backup = (String) event.getSession().getAttribute("identifier"); 
    } 

    @Override 
    public void valueUnbound(HttpSessionBindingEvent event) { 
     //Mainly for debuggin what happens to the session 
     if (ContextHolder.getId() != null) { 
      backup = ContextHolder.getId(); 
     } 
    } 

} 

class ContextHolder { 

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 

    public ContextHolder() { 
    } 

    public static void setId(String identifier) { 
     if (null == identifier) { 
      //throw some exception 
     } 
     contextHolder.set(identifier); 
    } 

    public static String getId() { 
     return (String) contextHolder.get(); 
    } 

    public static void clearId() { 
     contextHolder.remove(); 
    } 

    public static void setDefaultId() { 
     ContextHolder.clearId(); 
     contextHolder.set('init'); 
    } 
} 
+0

我懷疑併發的多個會話使用同一個線程。我猜這是在使用後沒有清理資源(ThreadLocal)的問題。您可以將代碼發佈到您的過濾器嗎?另外請確保您不要將引用存儲在單例中的某處。 –

+0

你可能是對的,我會更加註意清理資源。已經添加了示例代碼。關於'不要以單身存儲引用',我不知道我明白這一點。請查看ContextHolder,我需要在很多其他地方使用標識符,並且依賴於上下文持有者的相同。 – BST

+1

你應該把代碼包裝在try/finally塊中,在finally塊中做一個'ContextHolder.clearId()'來清除上下文持有者。這一點很重要,因爲請求處理線程被重用,這意味着ContextHolder的線程本地保留了與以前相同的id。 –

回答

1

你應該換你的代碼在一個try/finally塊,在finally塊做清除上下文持有人ContextHolder.clearId()。這一點很重要,因爲請求處理線程被重用,這意味着ContextHolders線程本地保留了與以前相同的id。 - M. Deinum

這樣做解決了這個問題。