Spring注入全局的HttpServletRequest

Spring的Controller默认是单例的,也就是说,如果Controller中有个field使用@Autowired自动注入,那么注入后这个field的值在该Controller中是全局的,不会再改变的(手动修改除外)。
在Controller中,很多方法都会用到HttpServletRequest,一般会在方法的参数中写上HttpServletRequest,Spring会自动将这个参数传进来。如果大量的方法都需要这个参数,可以将这个参数定义在Controller的一个field中。

@Controller
@RequestMapping("/")
public class HomeAction {

    @RequestMapping(value = {"", "/"})
    public String index(HttpServletRequest request, HttpServletResponse       response) {
//do something
    }

    @RequestMapping(value = {"list"})
    public String list(HttpServletRequest request, HttpServletResponse response) {
//do something
    }
}

可以使用这种方式来改写,这样所有的方法中都可以直接使用HttpServletRequest:

@Controller
@RequestMapping("/")
public class HomeAction {
    @Autowired
    private HttpServletRequest request;

    @RequestMapping(value = {"", "/"})
    public String index(HttpServletResponse  response) {
//do something
    }

    @RequestMapping(value = "list")
    public String list(HttpServletResponse response) {
//do something
    }
}

这里第一感觉会有点奇怪,因为在HomeAction实例化的时候,HttpServletRequest就已经设置进去了,而且在该Controller实例的整个生命周期内,HttpServletRequest的值都不会变化,那么在多线程的时候,怎么能保证每次使用的HttpServletRequest都能正确对应到当前的http请求呢?
Spring注入的这个HttpServletRequest,其实只是一个代理类,每次调用该request的方法时,会先使用RequestContextHolder.currentRequestAttributes().getRequest() 来获取到当前的实际http请求,然后再执行该实际request的对应方法。

这是一个非常好的使用代理模式的例子,代理类可以全局唯一,但是具体实现类可以有很多不同的实例。

除了在Controller中可以注入HttpServletRequest,在拦截器中也可以进行注入,这样拦截器中可以使用的信息就非常多了。

public class SomeInterceptor implements MethodInterceptor {

    @Autowired
    private HttpServletRequest request;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
      //do something
    }
}

除了HttpServletRequest外,还有很多其他内容可以通过Spring自动注入,例如:

  • ServletRequest
  • ServletResponse
  • HttpSession
  • Principal
  • Locale
  • InputStream
  • Reader
  • OutputStream
  • Writer

以上Spring能自动注入的列表摘自AnnotationMethodHandlerAdapter.resolveStandardArgument

发表评论

电子邮件地址不会被公开。 必填项已用*标注