Spring: How to Inject an Httpservletrequest into a Request-Scoped Bean

Spring: how do I inject an HttpServletRequest into a request-scoped bean?

Request-scoped beans can be autowired with the request object.

private @Autowired HttpServletRequest request;

Spring define a request scoped bean that has access to the request object

You have forgotten @Component to ask Spring to consider that class a bean. Then you need @Autowired not @Inject to ask for HttpServletRequest to be injected. According to documentation all spring beans of Request scope can ask for HttpServletRequest to be injected.

As for provider you don't even need it to read that request. You can read it directly from HttpServletRequest

@Component
@RequestScoped
public class CurrentUserProvider implements Provider<User> {

private static final String CURRENT_USER_HEADER = "x-current-user";

@Autowired
private HttpServletRequest httpServletRequest;

@Override
public User get() {
return new User(httpServletRequest.getHeader(CURRENT_USER_HEADER));
}

}

Spring Boot HttpServletRequest Request Scope Bean?

The HttpServletRequest is normally created and managed by servlet container (e.g. Tomcat) but not Spring. You normally do not need to define it as a spring bean unless you are doing something special. So technically from the spring 's point of view, the HttpServletRequest does not have any scope as it is not a spring bean.

But even if you need to define a HttpServletRequest bean for some reason, by default it will be in the singleton scope. (see the scope table in this).

The relationship between HttpServletRequest and a request scope bean is that Spring will make sure whenever the servlet container process a new HttpServletRequest , it will create a new request scope bean instance in case you need to access it during processing this HttpServletRequest. And this request scope bean will be destroyed after the servlet container finish process that HttpServletRequest. Such behaviour is also mentioned in the above link as :

Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext

How to inject current HttpServletRequest into any Spring service?

You can get HttpServletRequest in this way if you don't want to use @Autowired in the controller:

HttpServletRequest request = 
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();

HttpServletRequest injection on RequestScoped bean CDI

A late answer on this one--maybe useful to other readers: dependency injection in CDI is done in the following order:

  1. the constructor is invoked
  2. fields are injected
  3. @PostConstruct annotated method is invoked

The last point is where you want to step in for further initialization that needs access on the injected fields:

@Inject HttpServletRequest request;

public CurrentTransaction() {
// field injection has not yet taken place here
}

@PostConstruct
public void init() {
// the injected request is now available
this.user = request.getHeader("user");
this.token = request.getHeader("token");
}

Spring promoting request scoped bean to child threads (HttpServletRequest)

For any future adventurers:

I took some time to dig through the Spring code and found RequestContextHolder that has a inheritableRequestAttributesHolder. If you look at the documentation of what that is (inheriting from: InheritableThreadLocal) one can read the following:

Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.

So the RequestContextHolder has a field for that and actually setRequestAttributes supports a flag to use inheritableRequestAttributesHolder. Furthermore if you look at RequestContextListener -> requestInitialized you find that it is called without the flag (= false). So what I ended up doing is this:

public class InheritableRequestContextListener extends RequestContextListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
InheritableRequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";

@Override
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes, true);
}
}

And voila, I can access SessionContextProvider in child threads.

Access a request scoped Bean in Service

As long as the bean is declared as request scope, Spring will take care of the rest.

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestContext requestContext() {
return new RequestContext();
}

Access the bean in the usual way, just autowire it.

@Autowired
private RequestContext requestContext;

The Service bean will be a sigleton but under the covers the RequestContext bean is attached to the thread so you will get a different instance each time a method is called.

NOTE YOU MUST HAVE A WEB CONTEXT, i.e. RUNNING A WEB SERVER/WEB APP

Inject HttpServletRequest into Controller

No, for HttpServletRequest it will not be a problem and it shouldn't for other request scoped beans. Basically, Spring will generate a proxy HttpServletRequest that wraps some kind of ObjectFactory (RequestObjectFactory for HttpServletRequest) (YMMV) that knows how to retrieve the actual instance. When you use any of the methods of this proxy, they will delegate to that instance.

What's more, this is done lazily, so it won't fail at initialization. It will however fail if you try to use the bean when there is no request available (or if you haven't registered the RequestScope).


The following is in response to the comments and to clarify in general.

Regarding the proxy-mode attribute of @Scope or the XML equivalent, the default is ScopedProxyMode.NO. However, as the javadoc states

This proxy-mode is not typically useful when used with a non-singleton
scoped instance
, which should favor the use of the INTERFACES or
TARGET_CLASS proxy-modes instead if it is to be used as a dependency.

With request scoped beans, this proxy-mode value will not work. You'll need to use INTERFACES OR TARGET_CLASS depending on the configuration you want.

With scope set to request (use the constant WebApplicationContext.SCOPE_REQUEST), Spring will use RequestScope which

Relies on a thread-bound RequestAttributes instance, which can be
exported through RequestContextListener, RequestContextFilter or
DispatcherServlet.

Let's take this simple example

@Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES, value = WebApplicationContext.SCOPE_REQUEST)
public class RequestScopedBean {
public void method() {}
}
...
@Autowired
private RequestScopedBean bean;

Spring will generate two bean definitions: one for your injected bean, a singleton, and one for the request scoped bean to be generated on each request.

From those bean definitions, Spring will initialize the singleton as a proxy with the types of your target class. In this example, that is RequestScopedBean. The proxy will contain the state it needs to produce or return the actual bean when it is needed, ie. when a method is called on the proxy. For example, when

bean.method();

is called.

This state is basically a reference to the underlying BeanFactory and the name of the request-scoped bean definition. It will use these two to generate a new bean and then call method() on that instance.

The documentation states

The Spring IoC container manages not only the instantiation of your
objects (beans), but also the wiring up of collaborators (or
dependencies). If you want to inject (for example) an HTTP request
scoped bean into another bean, you must inject an AOP proxy in place
of the scoped bean.
That is, you need to inject a proxy object that
exposes the same public interface as the scoped object but that can
also retrieve the real, target object from the relevant scope (for
example, an HTTP request) and delegate method calls onto the real
object.

All eagerly loaded request scoped beans, if implemented correctly, will be proxies. Similarly, request scoped beans that aren't eagerly loaded will either be proxies themselves or be loaded through a proxy. This will fail if there is no HttpSerlvetRequest bound to the current thread. Basically, a proxy is necessary somewhere in the bean dependency chain for request scoped beans.



Related Topics



Leave a reply



Submit