Preface
Note that in order to save code, HttpServletRequest was not declared in the method body, and the drilled pit was directly injected with autowire.
Conclusion: For those who are anxious. Declare HttpServletRequest directly on the Controller's member variable using @Autowire, which is thread safe!
@Controllerpublic class TestController{ @Autowire HttpServletRequest request; @RequestMapping("/") public void test(){ request.getAttribute("uid"); }}The conclusion is as above.
background
This is true. Since I added authentication information to the head of the Request in the project, and after the interceptor intercepts the information and passes the verification, I will add the current user's identity to the request's Attribute, which is convenient for reusing it in the Controller layer.
Question: Why not use @RequestHeader to retrieve it directly on the Controller? Because the header contains encrypted data and requires some complex authentication and judgment, this step is directly thrown into the interceptor for execution.
So after decryption, I set the user information (such as uid) into request.setAttribute() to extract it in the Controller.
If you need to use request, you generally need to declare it on the method, such as:
public Result save(HttpServletRequest request){ // dosomething();}So, if I use uid for each method, wouldn’t every method declare a request parameter, in order to save a redundant step. I wrote a base class.
public class CommonController{ @Autowire HttpServletReqeust request; public String getUid(){ return (String)request.getAttribute("uid"); }}Later I was worried that because the controller is a singleton, will this result in the subsequent reqeust covering the previous request, and there are thread safety issues under concurrency conditions. So I asked a question on segmentFault, and most netizens said that there is indeed a threading problem! segmentFault problem address### verification process Because most netizens’ opinions are that they can only declare them in terms of methods, I naturally don’t want to give up writing so much code just now, so I started my verification process. The enthusiastic programmers have provided me with several solutions. Since I have put in the effort to prove it, I will put the results here and share them with you.
Method 1
The first method is to display the declaration HttpServletReqeust in the controller method, the code is as follows:
@RequestMapping("/test")@RestControllerpublic class CTest { Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping("/iiii") public String test(HttpServletRequest request) { logger.info(request.hashCode() + ""); return null; }}Press F5 in the browser
Output
I was confused at that time, **I agreed to be thread safety! **Isn't this the same request? Are you kidding me! I have been searching for a long time for request to rewrite hashcode()!
Ah, this is the truth, because I press F5 with my browser, and no matter how hard I press it, I can't simulate concurrency. That's equivalent to the server using the same thread to process my request. As for the hashcode of this request, according to jdk, it is calculated based on the virtual address of obj in jvm. I guess what's next. If you know what you really want, I'll tell me!
guess
The memory space of the request applied for by each thread in the server is fixed when the server is started. Then every time I request, he will create a new request in the memory space he requested (maybe a structure similar to an array). (Similar to the starting point of the array is always the same memory address). Then I initiate a request, and he will create a new request at the start position and pass it to the servlet and start processing. After the processing is completed, it will be destroyed. Then the new request created by his next request is destroyed, so it starts from the starting address. In this way, everything will be explained!
The guess is finished
Verify conjecture:
I won't let him have time to destroy it, can I test the code
@RequestMapping("/test")@RestControllerpublic class CTest { Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping("/oooo") public String testA(HttpServletRequest request) throws Exception { Thread.sleep(3000); logger.info(request.hashCode() + ""); logger.info(reqeust.getHeader("uid"); return null; } @RequestMapping("/iiii") public String test(HttpServletRequest request) { logger.info(request.hashCode() + ""); logger.info(reqeust.getHeader("uid"); return null; }}As mentioned above, I sleep in the interface/oooo for 3 seconds. If it shares a reqeust, then the subsequent request will overwrite the reqeust in the sleep, and the incoming uid is the interface address. Start /oooo first and then start /iiii
Output
controller.CTest:33 - 364716268controller.CTest:34 - iiiicontroller.CTest:26 - 1892130707controller.CTest:27 - ooooo
Conclusion: 1. The /iii initiated later does not overwrite the previous /oooo data and there is no thread safety issue. 2. The hashcode of the request is different. Because /oooo blocking causes another thread to need to process it, it creates a new request instead of the same hashcode as before.
Second round of verification
public class HttpTest { public static void main(String[] args) throws Exception { for (int i = 300; i > 0; i--) { final int finalI = i; new Thread() { @Override public void run() { System.out.println("v###" + finalI); HttpRequest.get("http://localhost:8080/test/iiii?").header("uid", "v###" + finalI).send(); } }.start(); } }}Under simulated concurrency conditions, uid300 in header fully accepts without overriding
So this way, there is no thread safety problem.
Method 2
In CommonController, use @ModelAttribute to handle.
public class CommonController {// @Autowired protected HttpServletRequest request; @ModelAttribute public void bindreq(HttpServletRequest request) { this.request = request; } protected String getUid() { System.out.println(request.toString()); return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid"); }}This has thread safety issues! The subsequent request may cover the previous one!
Verification code
@RestController@RequestMapping("/test")public class CTest extends CommonController { Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping("/iiii") public String test() { logger.info(request.getHeader("uid")); return null; }} public class HttpTest { public static void main(String[] args) throws Exception { for (int i = 100; i > 0; i--) { final int finalI = i; new Thread() { @Override public void run() { System.out.println("v###" + finalI); HttpRequest.get("http://localhost:8080/test/iiii").header("uid", "v###" + finalI).send(); } }.start(); } }}Partial output results were intercepted
controller.CTest:26 - v###52controller.CTest:26 - v###13controller.CTest:26 - v###57controller.CTest:26 - v###57controller.CTest:26 - v###21controller.CTest:26 - v###10controller.CTest:26 - v###82controller.CTest:26 - v###82controller.CTest:26 - v###93controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###85controller.CTest:26 - v###85controller.CTest:26 - v###14controller.CTest:26 - v###47controller.CTest:26 - v###47controller.CTest:26 - v###69controller.CTest:26 - v###22controller.CTest:26 - v###55controller.CTest:26 - v###61
You can see that 57, 71, 85, and 47 are covered, and some requests are lost!
Doing so is thread-insecure!
Method 3
Use CommonController as the base class to request Autowire.
public class CommonController { @Autowired protected HttpServletRequest request; protected String getUid() { System.out.println(request.toString()); return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid"); }}The test interface is the same as above, and the results are gratifying! There is no coverage for 100 requests. I increased the scope and tested it five or six times, and no coverage for thousands of requests. This can prove that there is no thread safety problem in this writing method!
Another interesting thing is that no matter how much concurrency is used, the hashcode of the request is always the same. Moreover, when testing different interfaces in the same Controller, it is the same. When using sleep to force block, the hashcode is the same. However, the hashcode is different when accessing different controllers, so I didn't continue to dig deeper into how to implement it.
But the conclusion is out, just as the article said at the beginning.
Summarize
The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.