This article describes several ways to get the request object in a Web system developed by Spring MVC, and discusses its thread safety.
I. Overview
When developing a web system using spring MVC, it is often necessary to use the request object when processing requests, such as obtaining the client IP address, the requested URL, the attributes in the header (such as cookies, authorization information), the data in the body, and so on. Because the controller, service, and other objects that handle requests are singleton in spring MVC, the most important thing to be aware of when fetching the request object is whether the request object is thread-safe: when there are a large number of concurrent requests, can you guarantee different requests/ Different request objects are used in the thread.
Here's another question to note: What is the use of the request object in the previous "when processing requests"? Given the slightly different ways to get the request object, there are generally two categories:
1. Use the Request object in Spring beans: Includes both the controller, Service, repository and other MVC beans, as well as common spring beans such as component. To facilitate the explanation, the bean in spring is referred to as the Bean in the following article.
2. Use the Request object in a non-bean, such as a method in a normal Java object, or in a static method of a class.
In addition, this article discusses the request object on behalf of the expansion, but the same method applies to response objects, Inputstream/reader, Outputstream/writer, etc., where inputstream/ Reader can read the data in the request, and Outputstream/writer can write data to the response.
Finally, the method of obtaining the request object is also related to the version of Spring and MVC, this article is based on Spring4, and the experiment is done using the 4.1.1 version.
Second, how to test thread safety
Since the thread safety issue of the request object requires special attention, for later discussion, here's how to test if the request object is thread safe.
The basic idea of testing is to simulate a large number of concurrent requests from the client and then determine whether the requests use the same request object on the server.
The most straightforward way to determine if the request object is the same is to print out the address of the request object, if the same is the same object used. However, in almost all Web server implementations, the thread pool is used, which leads to two requests that arrive in the same thread: After the previous request processing is complete, the thread pool reclaims the threads and assigns the thread back to the subsequent requests. In the same thread, the request object used is likely to be the same (with the same address and different attributes). Therefore, even for thread-safe methods, different requests may use the same Request object address.
To avoid this problem, one method is to have the thread hibernate for a few seconds during the request processing, which allows each thread to work long enough to avoid assigning the same thread to a different request, and the other is to use the other properties of request (such as parameters, headers, Body, etc.) is a thread-safe basis for request, because even if different requests use the same thread (the Request object address is the same), the use of the request object is thread-safe as long as a different attribute is used to construct the request object two times. This article uses the second method of testing.
The client test code is as follows (create 1000 threads to send the request separately):
The controller code in the server is as follows (temporarily omitting the code to get the request object):
If the request object is thread-safe, the print results in the server are as follows:
If there is a thread safety issue, the print results in the server might look like the following:
If no special instructions are specified, the test code will be omitted from the code later in this article.
Third, the method 1:controller in addition parameter
1. code example
This method is the simplest to implement directly on the controller code:
This approach is implemented by the principle that spring assigns the request object to the method parameter when the Controller method begins to process requests. In addition to the request object, there are a number of parameters that can be obtained by this method, as described in: https://docs.spring.io/spring/docs/current/spring-framework-reference/ Web.html#mvc-ann-methods
After acquiring the request object in the controller, if you want to use the request object in other methods, such as service methods, tool class methods, and so on, you need to pass the request object as a parameter when calling these methods.
2. Thread Safety
Test Result: Thread safety
Analysis: At this point the Request object is a method parameter, which is equivalent to a local variable, which is undoubtedly thread-safe. A thread-safe Map can click here to view this article.
3. Advantages and Disadvantages
The main disadvantage of this approach is that the request object is too redundant to write, mainly in two points:
(1) If the request object is required in multiple controller methods, you need to add the request parameter in each method
(2) The request object can only be obtained from the controller, if the request object is used in a place where the function call level is deep, then all methods on the entire call chain need to add the request parameter
In fact, the request object is always the same throughout the processing of requests, which means that, in addition to special cases such as timers, the request object is equivalent to a global variable inside the thread. And this method, equivalent to this global variable, is transmitted. Click here to see the full set of Spring Series free technical tutorials for the public.
Iv. Method 2: Automatic injection
1. code example
First on the code:
2. Thread Safety
Test Result: Thread safety
Analysis: In spring, the controller's scope is singleton (singleton), that is, in the entire web system, there is only one testcontroller, but the request injected is thread-safe, because:
In this way, when the bean (TestController of this example) is initialized, spring does not inject a request object, but instead injects a proxy, and when the request object needs to be used in the bean, Gets the request object from the proxy.
This implementation is described in detail in the following code.
Add a breakpoint to the above code to see the properties of the request object, as shown in:
As you can see in the diagram, the request is actually a proxy: the implementation of the proxy is described in Autowireutils's internal class Objectfactorydelegatinginvocationhandler:
That is, when we invoke the method of request, we are actually invoking the method of the object generated by Objectfactory.getobject (); Objectfactory.getobject () The generated object is the real request object.
Continuing to observe, it is found that the type of objectfactory is Webapplicationcontextutils's inner class requestobjectfactory, while the Requestobjectfactory code is as follows:
Where the request object needs to call the Currentrequestattributes () method to obtain the Requestattributes object, the implementation of this method is as follows:
The core code for generating the Requestattributes object is in class Requestcontextholder, where the relevant code is as follows (omitting extraneous code in the Class):
As you can see from this code, the generated Requestattributes object is a thread local variable (ThreadLocal), so the request object is also a thread-local variable, which guarantees thread security for the request object. Click here to see the full set of Spring Series free technical tutorials for the public.
3. Advantages and Disadvantages
The main advantages of this method are:
(1) Injection is not limited to controller: In Method 1, only the request parameter can be added to the controller. For method 2, not only can the controller be injected, but it can also be injected into any bean, including service, repository, and ordinary beans.
(2) The injected object is not limited to request: In addition to injecting the request object, this method can also inject other objects, such as Response object, session object and so on, which scope is request or session, and ensure thread safety.
(3) Reduce code redundancy: simply inject the request object into the bean that needs the request object, and you can use it in each of the bean's methods, which greatly reduces code redundancy compared to Method 1.
However, the method also has code redundancy. Consider this scenario: there are many controllers in the Web system, and the request object is used in each controller (which is actually very frequent), and you need to write the code that injects the request many times, and if you need to inject response, The code is more cumbersome. The following describes an improved approach to the automatic injection method and analyzes its thread safety and pros and cons.
V. Method 3: Automatic injection in the base class
1. code example
The injected portion of the code is placed in the base class, as compared to Method 2.
Base class Code:
The controller code is as follows; the two derived classes of Basecontroller are listed here, because the test code will be different at this time, so the server test code is not omitted, and the client needs to make corresponding modifications (while sending a large number of concurrent requests to 2 URLs).
2. Thread Safety
Test Result: Thread safety
Analysis: In understanding the thread safety of Method 2, it is easy to understand that method 3 is thread-safe: When creating different derived class objects, the fields in the base class (here is the injected request) occupy different memory spaces in different derived class objects. In other words, placing the code that injects the request into the base class has no effect on thread safety, and the test results prove it. A thread-safe Map can click here to view this article.
3. Advantages and Disadvantages
Instead of repeating the request in different controllers as compared to Method 2, the method is no longer useful when the controller needs to inherit other classes, given that Java only allows inheriting one base class.
Both Method 2 and Method 3 can only inject request into the bean, and if other methods (such as the static method in the tool class) need to use the request object, you need to pass in the request parameter when calling these methods. As described in Method 4 below, you can use the request object directly in a static method such as in a tool class (which can also be used in various beans, of course). Click here to see the full set of Spring Series free technical tutorials for the public.
Vi. Method 4: Call Manually
1. code example
2. Thread Safety
Test Result: Thread safety
Analysis: This method is similar to Method 2 (automatic injection), except that the method 2 is implemented by automatic injection, and this method is implemented by manual method invocation. Therefore, this method is also thread-safe.
3. Advantages and Disadvantages
Pros: can be obtained directly from non-beans. Cons: The code is cumbersome if you use more places, so you can use it with other methods.
Vii. Method 5: @ModelAttribute method
1. code example
The following method and its variants (variants: placing request and bindrequest in subclasses) are often seen on the Web:
2. Thread Safety
Test Result: Thread insecure
Analysis: @ModelAttribute annotations are used when modifying a method in a controller that is executed before each @requestmapping method in the controller executes. So in this case, Bindrequest () assigns a value to the Request object before test () executes. Although the parameter request in Bindrequest () itself is thread-safe, because TestController is a singleton, request is not guaranteed to be thread-safe as a domain of TestController.
Viii. Summary
In summary, the controller in addition to the parameters (Method 1), Automatic injection (Method 2 and Method 3), manual Invocation (method 4) are thread-safe, can be used to obtain the request object. If the request object is used less in the system, it can be used in whichever way, and automatic injection (method 2 and Method 3) is recommended to reduce code redundancy if more is used. If you need to use the request object in a non-bean, you can either pass in the parameter at the top of the call, or you can get it directly in the method by calling it manually (method 4).
Several ways Spring obtains request and its thread security analysis