DispatcherServlet中的ThreadLocal

时间:2016-02-03 11:54:52

标签: java spring spring-mvc thread-local

我有一个带有javascript UI的Spring MVC(v4.1.3)Web应用程序。我已经实现了一个自定义DispatcherServlet并在web.xml中配置了相同的

有一个唯一的屏幕代码,在UI向服务器发出的每个请求的HTTP标头中发送。

在我的自定义调度程序servlet的doService方法中,我捕获HTTP标头并将值放在ThreadLocal dto变量中。我在服务层中访问此ThreadLocal变量,以执行一些对所有请求都通用的审计逻辑。

CustomDispatcherServlet中的代码:

<div id="ball" class="dd">
<div id="ball">

if ($('div').attr('class') != undefined){
        alert('YES');
    }

服务层代码:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String uiCode = request.getHeader("uiCode");

    if ((uiCode != null && !uiCode.trim().isEmpty())) {
        UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
        final ThreadLocal<UiCodeDto> threadLocalUser = new ThreadLocal<UiCodeDto>();
        threadLocalUser.set(uiCodeDto);
    }

    ...
    super.doService(request, response);
}

ThreadLocalUtil代码,用于从ThreadLocal中检索值:

UiCodeDto temp = ThreadLocalUtil.getUiCodeDto(Thread.currentThread());

问题如下 - 1.我得到屏幕代码的随机值 - 这意味着一些http请求N的值来自http请求N + 1。 2. ThreadLocal变量中存在具有相同名称的空DTO - 因此,有时当我访问服务层中的ThreadLocal时,我得到一个空

我需要帮助理解DispatcherServlet中ThreadLocal的行为 - 为什么它会在doService方法中获取另一个请求的值?

提前致谢。

2 个答案:

答案 0 :(得分:2)

您的代码容易出错且难以理解,为什么还需要自定义DispatcherServlet。过滤器似乎更适合此任务。

public class UiCodeFilter extends OncePerRequestFilter {

    protected void doFilterInternally(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
        try {
            String uiCode = req.getHeader("uiCode");

            if ((uiCode != null && !uiCode.trim().isEmpty())) {
                UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
                UiCodeHolder.set(uiCodeDta);
            }
            chain.doFilter(req, res);
        } finally {
            UiCodeHolder.clear(); // Always clear!
        }

    }
}

UiCodeHolder有一个static ThreadLocal来保留该值。

public abstract class UiCodeHolder {
    static ThreadLocal<UiCodeDto> current = new ThreadLocal<>()

    public void set(UiCodeDto uiCode) {
        current.set(uiCode);
    }

    public UiCodeDta get() {
        return current.get();
    }

    public void clear() {
        current.remove(); // for older versions use current.set(null);
    }
}

在您的服务中,您现在只需UiContextHolder.get()即可获得正确的值。 UiCodeFilter负责设置值,并在请求结束时再次清除该值以防止泄漏。

这种方法并不需要丑陋的反射钩子,很容易理解,Spring,Hibernate和框架都使用它。

答案 1 :(得分:0)

Spring的另一种方法是使用request-scoped bean来提取并保留标题:

@Component
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UiCodeDto {
    private String uiCode;

    @Inject
    public void setCode(HttpServletRequest req) {
        uiCode = req.getHeader("uiCode");
    }

    public String getUiCode() {
        return uiCode;
    }
}

你可以像普通的豆一样使用它:

@Service
public class RandomService {

    @Inject
    UiCodeDto uiCodeDto;

    public void handle() {
        System.out.println(uiCodeDto.getUiCode());
    }
}