25

Assume we have defined a controller class which has @Controller annotation only.

Inside the class, we have defined private @Autowired HttpServletRequest request; variable.

Spring Controllers are Singleton. When we defined HttpServletRequest as @Autowired in a web-application, will it be an issue?

I read from a web-site that even though it is @Autowired it just injects a proxy for the thread variable.

Is it true? In a multi-threaded environment can we use @Autowired or passing HttpServletRequest as a parameter to each method in the controller class would be the right approach?

Some websites says it is an issue and suggested to pass as a parameter whereas few say it will be an issue.

I don't understand which one is right.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Learner
  • 253
  • 1
  • 3
  • 6

2 Answers2

48

Both are ok.
@Autowired HttpServletRequest and passing as a parameter are the same things.

Before passing HttpServletRequest to invocation method responding to @RequestMapping function, Spring stores the HttpServletRequest into a ThreadLocal type variable.

That ThreadLocal variable is a thread-safe map that keeps HttpServletRequest in the current thread context. The @Autowired HttpServletRequest proxy bean gets the correct request from that ThreadLocal variable.

rohanagarwal
  • 771
  • 9
  • 30
dknight
  • 619
  • 6
  • 7
  • Thanks a lot for the confirmation – Learner Feb 02 '18 at 03:44
  • 12
    Can you please update your answer with reference to documentation that supportings your `thread-safe` explanation? – Raf Dec 09 '18 at 20:31
  • 1
    @Raf, this can be confirmed too by looking at the source code beginning from `org.springframework.web.context.support.WebApplicationContextUtils.RequestObjectFactory`. – Jaime Hablutzel Jan 31 '20 at 01:14
  • 1
    Now I wonder how it could work at other Spring beans than Controller. Especially when it comes to asynch invocations as well as circuit-breaker (semaphore) kinds, that's not ThreadLocal involved. After I check the hierarchy, I've learned StandardServletAsyncWebRequest exists, but still not sure of the best practices/examples. Any references or docs, please share with us? Thanks. – Brendan Kim Jan 26 '21 at 14:02
1

Answering an old and accepted question here.

comparison

The HttpServletRequest is one of the supported controller method arguments as specified in the Spring Web MVC docs

The @Autowired way, on which I don't find much on the official sites from my searching results. But many SO answers mention that "Spring stores the HttpServletRequest into a ThreadLocal variable ... it would be injected per thread" based on inspecting the source code.

some inspection I made

The assumption is reasonable, but where is the entry point? I tried printing the runtime class name of the autowired HttpServletRequest bean:

// ... imports ...

@RestController
@Slf4j
public class K {
  @Autowired private HttpServletRequest request;

  @GetMapping public ResponseEntity<String> getIt() {
    log.info("GET request rt class is {}", request.getClass().getName());
    return ResponseEntity.ok("k");
  }
}

The output is something like jdk.proxy2.$Proxy77, right, it's a proxy injected. Then should use Proxy.getInvocationHandler(request).getClass().getName(). Now the output is org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler, a private nested class within AutowireUtils, which has a field of type ObjectFactory. The ObjectFactory has 16 implementations (you can use an IDE to check it out), the most likely one (by assumption) that gives the ServletRequest is the private nested class RequestObjectFactory within WebApplicationContextUtils:

//...
private static ServletRequestAttributes currentRequestAttributes() {
    RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
    if (!(requestAttr instanceof ServletRequestAttributes)) {
        throw new IllegalStateException("Current request is not a servlet request");
    }
    return (ServletRequestAttributes) requestAttr;
}

/**
 * Factory that exposes the current request object on demand.
 */
@SuppressWarnings("serial")
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
    @Override
    public ServletRequest getObject() {
        return currentRequestAttributes().getRequest();
    }
    // ...
}
// ...

As you can see, it comes to the RequestContextHolder, a Holder class to expose the web request in the form of a thread-bound RequestAttributes object. Now it makes some sense.


See also

kiz
  • 47
  • 1
  • 8