1

我正在使用存储字符串的 ThreadLocal 对象。我正在将字符串值设置为过滤器中的 ThreadLocal 对象,该过滤器会拦截受特定条件约束的所有请求。此外,我将 ThreadLocal 的字符串值设置为 HttpSession 作为属性。

session中的属性被多个jsps使用,最终传递给业务层。

我面临的问题是,来自不同客户端的多个请求在某个时间点获得相同的字符串值,尽管会话不同。

所以我的理解是,多个会话访问同一个线程。我没有看到任何其他解释。

将属性设置为请求会导致在 jsps 之间移动时出现问题。由于spring security有重定向,这意味着请求属性丢失。

那么有什么办法可以改变实现,使多个会话不使用同一个线程?

编辑:添加示例代码

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');
    }
}
4

1 回答 1

1

您应该将代码包装在 try/finally 块中,在 finally 块中执行 ContextHolder.clearId() 来清除上下文持有者。这一点很重要,因为请求处理线程被重用,这意味着 ContextHolders 线程本地保留与以前相同的 id。– M. Deinum

执行上述操作解决了问题。

于 2014-02-27T08:42:04.887 回答