本文介绍 Spring Boot 2 获取 HttpServletRequest 的方法。
目录
- 概述
- 方法
- Controller 方法参数
- 属性自动注入
- 手动方法调用
- 借助
@ModelAttribute
注解
- 总结
概述
借助 Spring MVC 开发 Web 应用的过程中经常需要使用 HttpServletRequest 对象获取 HTTP 请求相关的信息,如:客户端 IP、HTTP Header 中的属性等。
使用 HttpServletRequest 对象的场景大体分为两类:
- 在 Spring 注入的 Bean 中使用;
- 在非 Spring 注入的 Bean 中使用,如普通的 Java 对象或类的静态方法。
方法
Controller 方法参数
原理:
在 Controller 方法开始处理请求时,Spring 会将 HttpServletRequest 对象自动赋值到方法参数中。除 HttpServletRequest 对象外,还有很多其它参数可以通过此方法获取,参考:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
代码示例:
@GetMapping("/method1")
public String method1(HttpServletRequest request) {
System.out.println("Request URI: " + request.getRequestURI());
return "Invoke HttpServletRequest by method param.";
}
线程安全性分析:
HttpServletRequest 对象作为方法参数,相当于局部变量,是 线程安全 的。
缺点:
- 代码冗余,每个 Controller 方法中都要有
HttpServletRequest request
参数; - HttpServletRequest 对象的获取必须从 Controller 开始,如果使用 HttpServletRequest 对象的地方在函数调用层级比较深的地方,那么整个函数调用链上的所有方法都要有
HttpServletRequest request
参数。实际上在请求处理的全过程中,HttpServletRequest 对象始终存在,相当于线程内部的一个全局变量。
属性自动注入
代码示例:
@Autowired
private HttpServletRequest autowiredRequest;
@GetMapping("/method2")
public String method2() {
System.out.println("Request URI: " + autowiredRequest.getRequestURI());
return "Invoke HttpServletRequest by @Autowired.";
}
线程安全性分析:
使用 @Autowired
注解时,Spring 实际注入的并非 HttpServletRequest 对象,而是一个代理 proxy(代理实现参考 AutowireUtils.ObjectFactoryDelegatingInvocationHandler
),当方法中需要使用 HttpServletRequest 对象时通过此代理获取,所以虽然 Controller 是个单例类,但通过此方法使用 HttpServletRequest 对象是 线程安全 的。
优点:
- 不局限于 Controller 中,可以在通过
@Component
、@Service
、@Repository
注解注入的其它 Bean 中使用; - 注入的对象不局限于 HttpServletRequest 对象,还可以注入其它
scope
为request
或session
的对象,如 HttpServletResponse、HttpSession 等,并保证线程安全; - 相比于【Controller 方法参数】方法,减少了代码冗余,只需要在需要使用 HttpServletRequest 对象的方法所属 Bean 中注入。
缺点:
- 如果存在很多 Bean 需要使用 HttpServletRequest 对象,则每个 Bean 都要维护一个使用
@Autowired
注解注入的HttpServletRequest request
属性,也存在一定代码冗余,可以定义一个公共父类维护这些自动注入的属性,但是继承本身也存在一定问题。
手动方法调用
代码示例:
@GetMapping("/method3")
public String method3() {
HttpServletRequest request =
((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
System.out.println("Request URI: " + request.getRequestURI());
return "Invoke HttpServletRequest by ServletRequestAttributes.";
}
线程安全性分析:
与【属性自动注入】线程安全性分析一致,属于 线程安全。
优点:
- 可以在非 Spring 注入的 Bean 中使用,如普通 Java 对象或静态方法。
缺点:
- 代码比较繁琐。
借助 @ModelAttribute
注解
代码示例:
@Autowired
private HttpServletRequest bindRequest;
@ModelAttribute
public void bindRequest(HttpServletRequest request) {
this.bindRequest = request;
}
@GetMapping("/method4")
public String method4() {
System.out.println("Request URI: " + bindRequest.getRequestURI());
return "Invoke HttpServletRequest by @ModelAttribute.";
}
线程安全性分析:
@ModelAttribute
注解修饰 Controller 方法时,作用是在 Controller 中每个处理请求的方法执行前执行,而 Controller 是单例,所以当同时处理多个请求时 HttpServletRequest 对象属性可能会被覆盖,所以是 线程不安全 的!
总结
全量代码:
package tutorial.spring.boot.mvc.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@RestController
public class DemoController {
@Autowired
private HttpServletRequest autowiredRequest;
@Autowired
private HttpServletRequest bindRequest;
@GetMapping("/method1")
public String method1(HttpServletRequest request) {
System.out.println("Request URI: " + request.getRequestURI());
return "Invoke HttpServletRequest by method param.";
}
@GetMapping("/method2")
public String method2() {
System.out.println("Request URI: " + autowiredRequest.getRequestURI());
return "Invoke HttpServletRequest by @Autowired.";
}
@GetMapping("/method3")
public String method3() {
HttpServletRequest request =
((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
System.out.println("Request URI: " + request.getRequestURI());
return "Invoke HttpServletRequest by ServletRequestAttributes.";
}
@ModelAttribute
public void bindRequest(HttpServletRequest request) {
this.bindRequest = request;
}
@GetMapping("/method4")
public String method4() {
System.out.println("Request URI: " + bindRequest.getRequestURI());
return "Invoke HttpServletRequest by @ModelAttribute.";
}
}