Acquisition and injection of HttpServletRequest in Spring @Autowired injection Request

Problem Description:

When the team reviewed the code recently, the team members found that HttpServletRequest was injected directly through @Autowired, so everyone had a question, HttpServletRequest is not a class in Spring, and without manual injection through @Bean, how does Spring To help developers complete the injection?

At the same time, we know that the bean injected by default in the ioc container is a singleton, and each request is independent, so will there be no problems?

In order to study and understand why, I found some information and wrote a simple demo to study it.

The Demo class is as follows:

/**
 * @author zhangrenwei
 * @date 2022/8/1
 * @description replace this
 **/
@RestController
public class HttpServletRequestTest {

    @Autowired
    private HttpServletRequest httpServletRequest;

    @GetMapping("/sayHi")
    public void sayHi(String name) {

        System.out.println(httpServletRequest.getRequestURL().toString());
        JSONObject res = new JSONObject();
        res.put("requestId", UUID.randomUUID().toString());
        res.put("datatime", DateUtils.getDatetime());
        // ReturnResult.get(res);

        System.out.println("hello: " +  name);
    }

    @PostConstruct
    public void after(){
        System.out.println(this.httpServletRequest);
    }

}

Doubt 1

As an external object, why can HttpServletRequest be obtained by injection in the Spring project?

The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.

According to the description of @PostConstruct, the @PostConstruct annotation is used in the method call after the dependency injection is executed. We set the breakpoint on line 19 of the above demo to view the situation after the instantiation of HttpServletRequest httpServletRequest.

From the figure above, we can see that the httpServletRequest object is a proxy object (org.springframework.web.context.support.WebApplicationContextUtils.RequestObjectFactory), which is a request object factory. Enter the WebApplicationContextUtils class, Command + F12, we found a method registerWebApplicationScopes that injects bean objects into the ConfigurableListableBeanFactory factory object.

This method has such a line of code, beanFactory.``registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()``);

Since HttpServletRequest is inherited from ServletRequest, it has attracted our attention here.

What is the registerResolvableDependency method of ConfigurableListableBeanFactory? Let's continue reading.

Registers a particular dependency type with the corresponding autowired value.

This applies to factory/context references that are considered autowireable but are not defined as beans in the factory

Seeing this, we understand that Spring can inject another completely different object value into a class, so that when this class is referenced from the ioc container, this object value is actually obtained. And the above description just responds, why we can complete its automatic assembly without manually defining HttpServletRequest, the secret is here. (To further expand thinking, look at the above two pictures together, except for ServletRequest.class, ServletResponse.class, HttpSession.class and WebRequest.class type objects, which are automatically injected by Spring and can be directly referenced by annotations).

Continuing to enter the registerResolvableDependency method, we found that the implementation of this method is to use the dependency type of ServletRequest.class as the key, and RequestObjectFactory as the value of the assembly, and put it into the resolvableDependencies map.

Doubt 2

We know the injection and reference of HttpServletRequest, so for each independent request, in a multi-threaded scenario, will HttpServletRequest have thread safety problems through automatic injection?

We already know that the object returned by the injected HttpServletRequest is actually the following

org.springframework.web.context.support.WebApplicationContextUtils.``RequestObjectFactory object, so how does it achieve thread safety?

We look at its specific implementation.

Click along the currentRequestAttributes() method, and finally find that its return value is from org.springframework.web.context.request.RequestContextHolder#requestAttributesHolder.

The requestAttributesHolder object is a ThreadLocal object, so we understand that the HttpServletRequest automatically injected by Spring can guarantee the uniqueness of the request. It turns out that it is bound to each thread and obtains the request information from ThreadLocal, thereby ensuring thread safety!

Let's expand it again. Since the request information is taken from ThreadLocal, how is the request information put in?

We found the set method of the requestAttributesHolder attribute, set a breakpoint here, restarted the system, and initiated an http request.

Finally, through the call stack information, we see org.springframework.web.filter.RequestContextFilter#doFilterInternal

The initContextHolder method is called, and the initContextHolder method calls the set method of the ThreadLocal requestAttributesHolder.

Each http request will enter the filter RequestContextFilter, and the request information will be set in the current thread in this filter. Here, all the mysteries are solved.

Summarize

Let's make a summary. The way to inject HttpServletRequest through annotations in the code is not the real HttpServletRequest, but a proxy object org.springframework.web.context.support that is automatically injected when the Spring project starts.`` WebApplicationContextUtils.RequestObjectFactory.

The object is mapped and associated with ServletRequest.class, and put into the map injected by the Spring management bean.

How to ensure the thread safety of the automatically injected HttpServlet request every time a request is made is equivalent to asking org.springframework.web.context.support.WebApplicationContextUtils.``RequestObjectFactory how to achieve thread safety. This class is just a simple object factory. The getObject method finally obtains the requested information from the ThreadLocal org.springframework.web.context.request.RequestContextHolder#requestAttributesHolder, thus achieving isolation between requests.

Reference : link 1 , link 2


If this article is helpful to you, I am very happy to help you.

Of course, if you feel that there is something in the article that makes you feel unreasonable, or there is an easier way to implement it, or there is something that you can’t understand, I hope you can point it out in the comments after reading it, and I will read it as soon as possible reply you.

Guess you like

Origin blog.csdn.net/chenthe1/article/details/130516703