An Alternative Approach to ThreadLocal Using Spring
Want to learn more about this alternative approach to using ThreadLocal? Click here to find out more about using ThreadLocal in the Spring Framework.
Join the DZone community and get the full member experience.Join For Free
In a blog post published some time ago, Multitenant Applications Using Spring Boot JPA Hibernate and Postgres, I included code set to and to retrieve from the tenant identifier, a discriminator for selecting its associated data source using a
- A context holder class for holding the tenant data:
- A Spring MVC interceptor, which could have been done using a servlet filter, to set and clear the tenant identifier:
- And somewhere in the application:
I normally try to avoid using ThreadLocal and would also advise to limit their usage. They can be handy, solve difficult problems but can also introduce memory leaks.
In this post, I will discuss how to use Spring’s
ThreadLocalTargetSource to prevent dealing directly with the dangerous ThreadLocal, while practicing dependency injection and proper mocking in unit tests.
Note: This approach would still use a
ThreadLocal reference, but it delegates setting, injecting, and clearing it to the Spring framework.
- Java 7 or 8
- Maven 3.3 or better
- Familiarity with the Spring Framework
Create the Demo App
This command will create a Maven project in a folder named
threadlocaltargetsource-demo with the actuator and web-related Spring Boot dependencies. Read on if you are interested in adding Spring Boot support using the bom approach.
Let’s take a look at the relevant classes included in this demo application:
This class serves as the tenant data holder.
This is where Spring’s beans are instantiated. —
tenantFilterRegistration() are straightforward.
TenantFilter() instantiates a servlet filter, and
tenantFilterRegistration() implements a Spring mechanism that allows dependencies to be injected in the TenantFilter.java, a regular servlet filter.
Tip: Read this post Implementing and Configuring Servlets, Filters, and Listeners in Spring Boot Applications for more information.
These other beans look interesting:
ThreadLocalTargetSourceis useful when you need an object, a TenantStore.java instance in this case, to be created for each incoming request. The target (a TenantStore.java object) will be instantiated only once in each thread and will get removed from each thread’s
destroy()is called, for instance when the application is shut down.
tenantStorebean is the target object stored in each thread. It is required to be prototype-scoped in the
AbstractPrototypeBasedTargetSource, parent class of the
proxiedThreadLocalTargetSourcebean. According to the documentation and source code comments,
TargetSourcesmust run in a
BeanFactorysince they need to call the
getBean()method to create a new prototype instance.
This class implements a simple API when a TenantStore.java instance is injected.
We will see later that there is only one instance of the TenantStore.java class for different requests, each holding different tenant data.
This servlet filter takes care of setting the tenant identifier to the TenantStore.java holder and clearing it up during the servlet filter chain’s way out.
We will see later how there is only one instance of the TenantStore.java class for different requests, each holding different tenant data.
Packaging and Running the App
This application can be run from your preferred IDE as a regular Java application or from a command line:
Or, we can perform the following:
Let’s place a breakpoint in the TenantFilter.java class:
Then, we can send a couple of simultaneous requests to the
And, we can also perform the following command:
Both requests should have been suspended in the Tenant filter breakpoint. Let's take a look at partial stack traces and attribute values:
First request to
tenantStore's id is
119 and that
tenantStore's tenantId is set to
Second request to
As for the second request,
tenantStore's id is also
119 but the
tenantStore's tenantId is set to
tenant_2. Interesting, eh? All of this happens while both requests are still being processed. Also, notice
http-nio-8080-exec-1’s stack trace corresponds to the processing request #1.
As the execution of the requests is resumed, the response to the first request looks like:
And, the response to the second request:
Let’s now see how a unit test for the DemoResource.java class would look:
The TenantStore dependency is being mocked using Mockito and injected in the API implementation. A case could be made to prevent using
@InjectMocks but that goes out of the scope of this post.
To how I used it earlier this year:
This is error-prone since the
ThreadLocal would have to be cleared in every test that follows this pattern to prevent a possible misuse if another unit test uses the same
ThreadLocal ran in the same thread.
And, that’s all! Thanks for reading, and as always, your feedback is very much appreciated. If you found this post helpful and would like to receive updates when content like this gets published, sign up to the newsletter.
The source code for this blog post can be found here.
Published at DZone with permission of Orlando Otero, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.