SpringMVC的Controller中使用线程安全的初始化
因为SpringMVC的Controller默认是单例, 在这种情况下, Controller中使用的私有变量必须也是单例, 例如各种service, 否则会有多线程访问数据互相修改的问题.
对于需要对一类Controller做相同初始化的需求, 可以通过@ModelAttribute("some-variable") public init() 这样的方法解决.
首先创建一个基类如BasicController, 构造一个拦截器, 对基类实现preHandle(), postHandle()方法
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception { RequestBean requestBean = (RequestBean)request.getAttribute(RequestBean.ATTR_KEY); if (requestBean != null) { requestBean.destroy(); request.removeAttribute(RequestBean.ATTR_KEY); long current = System.currentTimeMillis(); logger.debug("Page end: " + current + ", time: " + (current - requestBean.getTimestamp()) + " milliseconds"); } } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) throws Exception { if (handler instanceof HandlerMethod && mav != null) { HandlerMethod hm = (HandlerMethod)handler; if (BasicController.class.isAssignableFrom(hm.getBean().getClass())) { mav.addObject("sys_config", Config.getConfigs()); } } } public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long timestamp = System.currentTimeMillis(); logger.debug("Page start: " + timestamp); if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod)handler; if (BasicController.class.isAssignableFrom(hm.getBean().getClass())) { RequestBean requestBean = new RequestBean(request, response, timestamp); request.setAttribute(RequestBean.ATTR_KEY, requestBean); } } return true; }
然后在BasicController中, 通过如下形式对每次访问作初始化
@ModelAttribute("requestBean") public RequestBean initRequest(HttpServletRequest request) { return (RequestBean)request.getAttribute(RequestBean.ATTR_KEY); }
然后在Controller方法中, 就可以直接引用这个初始化好的变量了
@RequestMapping(value = {"/edit","/add"}, method = RequestMethod.GET) public String doEdit( @ModelAttribute("requestBean") RequestBean rb) { ... }
@ModelAttribute("requestBean")