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');
}
}
我懷疑併發的多個會話使用同一個線程。我猜這是在使用後沒有清理資源(ThreadLocal)的問題。您可以將代碼發佈到您的過濾器嗎?另外請確保您不要將引用存儲在單例中的某處。 –
你可能是對的,我會更加註意清理資源。已經添加了示例代碼。關於'不要以單身存儲引用',我不知道我明白這一點。請查看ContextHolder,我需要在很多其他地方使用標識符,並且依賴於上下文持有者的相同。 – BST
你應該把代碼包裝在try/finally塊中,在finally塊中做一個'ContextHolder.clearId()'來清除上下文持有者。這一點很重要,因爲請求處理線程被重用,這意味着ContextHolder的線程本地保留了與以前相同的id。 –