When developing a web application with Spring MVC, you want to make some data available throughout the current request, like authentication information, request identifier, etc. These data are injected into a request-scoped context, and destroyed after the request ends. There are several ways to achieve that, and this article will demonstrate how.
Use HttpServletRequest or WebRequest
Controller methods can delare an HttpServletRequest
typed argument. When it is invoked, Spring will pass in an instance that contains information specific to the current request, like path and headers. It also provides a pair of methods that gets and sets custom attributes. For instance, Spring itself uses it to store application context, locale and theme resolver.
1 |
|
We can certainly use it to store our own data, like in a Filter
that sets the user information.
1 |
|
Spring also provides the WebRequest
interface that abstracts away Java servlet class. Under the hood, they store data in the same place.
1 |
|
HttpServletRequest
can also be injected as a dependency. For example in a service class:
1 |
|
You may need some knowledge of Project Lombok to understand the code. In short, when Spring initializes this service bean, it passes in a proxy object of HttpServletRequest
. When getFromRequest
is invoked, the request
variable within will point to the current servlet request instance.
As we can see, using HttpServletRequest
is straightforward, but it has two disadvantages. First, it is not type safe, we need to cast the return value. Second, the service layer should not know of the HTTP request. The context information we pass to lower layers should be decoupled. These two problems can be solved by the next approach.
Annotate context bean with @RequestScope
The default Spring bean scope is singleton
, and there are other scopes like prototype
, request
, and session
. When marked with @RequestScope
, a new instance will be created for every HTTP request, and get destroyed accordingly.
1 |
|
When injected as a dependency, Spring also wraps it with a proxy object.
1 |
|
Now the service has a typed context object, and it is not coupled with the HTTP layer.
RequestContextHolder static method
There is a utility class RequestContextHolder
from which we can get the currentRequestAttributes
, latter is an implementation of RequestAttributes
interface with getAttribute
and setAttribute
methods. The difference is this interface can be used to extract request-scoped attributes (stored in HttpServletRequest
) and session-scoped attributes (in HttpSession
). The WebRequest
instance is actually backed by RequestAttributes
, so is the @RequestScope
annotation.
1 | public User getFromRequestContextHolder() { |
Since RequestContextHolder
is used via static methods, it is necessary to tackle the multithreading problems. The answer is obvious: ThreadLocal
.
1 | public abstract class RequestContextHolder { |
This gives us an idea of implementing the fourth approach, i.e. write our own thread-local request context.
Thread-local request context
Each servlet request is handled in a separate thread, so we can use a thread-local object to hold the request-scoped context.
1 |
|
Beware the thread that processes your request is borrowed from a thread pool, and you don’t want your previous request info leaking into the next, so let’s clean it up in the Filter
.
1 |
|
Example code can be found on GitHub.