我有一个带有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方法中获取另一个请求的值?
提前致谢。
答案 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());
}
}