原文网址:SpringBoot--Controller获取HttpServletRequest_IT利刃出鞘的博客-CSDN博客
简介
本文介绍SpringBoot如何在Controller中获取HttpServletRequest。
法1:Controller中加参数
简介
该方法实现的原理是,在Controller方法开始处理请求时,Spring会将request对象赋值到方法参数中。除了request对象,可以通过这种方法获取的参数还有很多。
Controller中获取request对象后,如果要在其他方法中(如service方法、工具类方法等)使用request对象,需要在调用这些方法时将request对象作为参数传入。
代码示例
这种方法实现最简单,直接上Controller代码:
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping
public class HelloController {
@GetMapping("/test1")
public String test1(HttpServletRequest request) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(request);
return request.toString();
}
}
线程安全性
是线程安全的。
测试:接口中睡眠,模拟多个请求同时处理
直接用“代码示例”中的代码
Postman开两个窗口,都访问:http://localhost:8080/test1
结果:(两个不同的request,可见线程安全的)
org.apache.catalina.connector.RequestFacade@4613eaaa
org.apache.catalina.connector.RequestFacade@492b9e2e
注意:如果不加睡眠,结果可能是:两个相同的request,因为如果它能处理过来,就会用同一个request去接收了。
法2:自动注入
代码示例
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping
public class HelloController {
@Autowired
private HttpServletRequest request;
@GetMapping("/test1")
public String test1() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(request);
return request.toString();
}
}
线程安全性
线程安全(不是测出来的,是看代码得出的)。
分析:在Spring中,Controller的scope是singleton(单例),也就是说在整个web系统中,只有一个TestController;但是其注入的request却是线程安全的。
测试:接口中睡眠,模拟多个请求同时处理
使用上边“代码示例”中的代码
Postman开两个窗口,都访问:http://localhost:8080/test1
结果:(这个结果很奇怪,只能去追寻源码。见:SpringMVC原理--Controller线程安全_IT利刃出鞘的博客-CSDN博客)
Current HttpServletRequest
Current HttpServletRequest
法3:基类中自动注入
说明
与方法2相比,将注入部分代码放入到了基类中。
代码示例
基类代码:
public class BaseController {
@Autowired
protected HttpServletRequest request;
}
Controller代码
这里列举了BaseController的两个派生类,由于此时测试代码会有所不同,因此服务端测试代码没有省略;客户端也需要进行相应的修改(同时向2个url发送大量并发请求)。
@Controller
public class TestController extends BaseController {
// 存储已有参数,用于判断参数value是否重复,从而判断线程是否安全
public static Set<String> set = new ConcurrentSkipListSet<>();
@RequestMapping("/test")
public void test() throws InterruptedException {
String value = request.getParameter("key");
// 判断线程安全
if (set.contains(value)) {
System.out.println(value + "\t重复出现,request并发不安全!");
} else {
System.out.println(value);
set.add(value);
}
// 模拟程序执行了一段时间
Thread.sleep(1000);
}
}
@Controller
public class Test2Controller extends BaseController {
@RequestMapping("/test2")
public void test2() throws InterruptedException {
String value = request.getParameter("key");
// 判断线程安全(与TestController使用一个set进行判断)
if (TestController.set.contains(value)) {
System.out.println(value + "\t重复出现,request并发不安全!");
} else {
System.out.println(value);
TestController.set.add(value);
}
// 模拟程序执行了一段时间
Thread.sleep(1000);
}
}
线程安全性
测试结果:线程安全
分析:在理解了方法2的线程安全性的基础上,很容易理解方法3是线程安全的:当创建不同的派生类对象时,基类中的域(这里是注入的request)在不同的派生类对象中会占据不同的内存空间,也就是说将注入request的代码放在基类中对线程安全性没有任何影响;测试结果也证明了这一点。
优缺点
与方法2相比,避免了在不同的Controller中重复注入request;但是考虑到java只允许继承一个基类,所以如果Controller需要继承其他类时,该方法便不再好用。
无论是方法2和方法3,都只能在Bean中注入request;如果其他方法(如工具类中static方法)需要使用request对象,则需要在调用这些方法时将request参数传递进去。下面介绍的方法4,则可以直接在诸如工具类中的static方法中使用request对象(当然在各种Bean中也可以使用)。
法4:@ModelAttribute
代码示例
下面这种方法及其变种(变种:将request和bindRequest放在子类中)在网上经常见到:
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping
public class HelloController {
protected HttpServletRequest request;
@ModelAttribute
public void bindreq(HttpServletRequest request) {
this.request = request;
}
@GetMapping("/test1")
public String test1() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(request);
return request.toString();
}
}
线程安全性
是线程安全的。
分析:@ModelAttribute注解用在Controller中修饰方法时,其作用是Controller中的每个@RequestMapping方法执行前,该方法都会执行。因此在本例中,bindRequest()的作用是在test()执行前为request对象赋值。
测试:接口中睡眠,模拟多个请求同时处理
使用上边“代码示例”中的代码
Postman开两个窗口,都访问:http://localhost:8080/test1
结果:(虽然结果是同一个request,但是是线程安全的。跟“注入request”类似)。
org.apache.catalina.connector.RequestFacade@3bb8d854
org.apache.catalina.connector.RequestFacade@3bb8d854