2

Today, I discovered that it is possible to autowire HttpServletRequest into arbitrary singleton Spring beans.

@Component
public class MyComponent {
    private final HttpServletRequest servletRequest;

    public MyComponent(HttpServletRequest servletRequest) {
        this.servletRequest = servletRequest;
    }
}

I haven't found an authoritative source, but from what I can tell from searching online, this is actually a wrapper that delegates to the actual HttpServletRequest of the running thread.

For instance, this blog post from 2012 states:

So when multiple requests come to this service, how is the correct httpServletRequest corresponding to the user request injected into this service object. The answer is that a real HttpServletRequest is not really injected, only a proxy is injected in. The proxy is internally a reference to RequestContextHolder which at some point binds the HttpServletRequest to a threadlocal variable.

It's unclear to me whether this is a core Spring feature, a feature of Spring MVC, or something provided by Spring Boot.

I have not had any luck finding an authoritative source (e.g. in the Spring documentation, the internals of Spring, or the Spring release notes) explaining that this works, is a supported feature, and explaining how it works. I'm guessing it's there, but I'm not having luck finding it. If I'm going to be using a feature such as this, I would much prefer to have this context so that I can reason about when and how best to use it, and when to avoid it.

Where is this feature documented?

Note: This is a similar question to Inject HttpServletRequest into Controller. However, that is asking about the pitfalls of this approach and how it works, and does not address where or how it's documented or otherwise specified.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
M. Justin
  • 14,487
  • 7
  • 91
  • 130

2 Answers2

1

That possibility of autowiring the proxy for HttpServletRequest's is a part of Spring MVC, and sadly it's not documented properly.

The best you can find about it is in some release notes of spring-web:3.0.0. For example, take a look at the release notes of 3.0.0.M4 and especially at this issue where Juergen Hoeller (one of the main developers of String) talks about how it works since this version.

Also, feel free to check the sources (WebApplicationContextUtils, RequestContextHolder), they're well documented.

amseager
  • 5,795
  • 4
  • 24
  • 47
  • It looks like the main entry point for wiring these beans is [`WebApplicationContextUtils.registerWebApplicationScopes(beanFactory)`](https://github.com/spring-projects/spring-framework/blob/5328184f3a22ed3d848f6004c50c721597e4d564/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java#L183) and its overload. This is called by the `*WebApplicationContext` classes in `spring-web` and by `ServletWebServerApplicationContext` in Spring Boot to add the beans to these application contexts. – M. Justin Dec 02 '20 at 20:55
0

The other answer you got covers where the feature is (not) documented; it also covers how supported the feature is, in that it points out that the feature has made it to its third major version now, which makes it seem unlikely that it'll go away soon.

You got me interested in how it works, though, so I figure I'll write up what I found out. This is in a barebones Spring Boot app using an @Autowired field (not constructor injection), but lots of it will apply in other use cases also. A detailed description with links to specific lines of code follows, but have an executive summary first: hardly any of the code used is specific to HttpServletRequest, and the code specific to HttpServletRequest is the simplest code involved. I'd be surprised if the functionality ever gets removed or altered; even if it does, you could, I think, recreate it yourself if you're willing to mess around with the application context pre-refresh.

So, details: while the context is refreshing, WebApplicationContextUtils goes to the DefaultListableBeanFactory and says "if someone asks you where they can get a ServletRequest, you can answer them using this RequestObjectFactory". Later during that same context refresh, that same bean factory is creating beans. When it gets to the @Component, it sees that it needs to autowire a HttpServletRequest. Because that's an interface, it constructs a java.lang.reflect.Proxy from the RequestObjectFactory and assigns that to the @Autowired field.

Later, the servlet container receives a request for the app's servlet and sends it through the app's filter chain. One of the filters in that chain is RequestContextFilter, which sets the request attributes of the RequestContextHolder. Note that that class uses ThreadLocals; since every request gets handled in a different thread, no other thread will ever mutate the request attributes.

Later yet (but while still handling the same request), the @Component's method gets called. It in turn calls a method on the @Autowired HttpServletRequest field. That field contains a proxy, so the method invocation gets dispatched to the invocation handler that the proxy was created with. (See the "constructs" link two paragraphs above.) The invocation handler invokes the method on whatever the RequestObjectFactory's getObject method returns; that method, finally, can access the request that the servlet container set.

neofelis
  • 610
  • 4
  • 5