How to share data across multiple components for a single request in a Spring Boot application?

Let’s say that you make a REST API request to a spring boot web application.

There is a security requirement that you need to log the user id across all the method calls in the application for this particular request.

Something like this:

public void invoke(SomeRequest request){

    logger.info("Executing invoke() for the user ",userId);

   ServiceRequest request = ....   //populate request to be sent to service
    service.executeService(servicerequest,userId);
}

So , if your application flow starts from a controller class and then goes inside a service class to do some business logic and then inside a repository class to do database operations , you need to pass this user id available at the controller class to every single method as a method parameter.

User id should be available in all these components:

Controller -> Service -> Repository

You may pass the user id to the service class like below:

public void executeService(ServiceRequest request, String userId){

    logger.info("Executing service method for user ",userId);

    repository.executeRepository(repositoryRequest,userId);
}

And in the repository class again like below:

public void executeRepository(RepositoryRequest request, String userId){

logger.info("Executing repository method for user ",userId);
}

The code looks redundant and doesn’t look like a good design.

Wouldn’t it be nice if you just gather the user id at the controller class and get it automatically for each method in the flow.

ThreadLocal in java comes to the rescue.

Java allows threads to have their own values through ThreadLocal class.

So if you create a ThreadLocal variable , each thread will have its own value.

So you can achieve thread safety without synchronization.

This is an alternative to locking variables during multi threading to ensure thread safety.

Let’s see how to use this feature to share data among different classes in a Spring Boot Web application.

Let’s say you have created a web app with 3 components:

Controller

Services

Repository

You fetch user details in controller and this is specific to each request.

Each request is processed by a separate thread by Spring.

And you want this user details to be available across all the three components.

One way to achieve this as already discussed is to pass the user id from Controller to Service and then to Repository.

Instead we can set a thread local variable and retrieve from it as shown in the below example.

Let’s create a UserUtil class which holds the user id:

package com.example.demo;

public class UserUtil {

	public static ThreadLocal<String> currentUser = new ThreadLocal<>();

}

As you notice it is a static variable since we want a single copy of it for each request.

Again we can’t just use an integer static variable here because different threads may access each other’s user id , in other words , there is no thread safety in using static variables.

Let’s now grab the user id in the controller class:

package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	Logger log = LoggerFactory.getLogger(TestController.class);

	@Autowired
	TestService service;

	@GetMapping("/test")
	public void test(@RequestParam("userId") String userId) {

		log.info("User in controller class " + userId);
		UserUtil.currentUser.set(userId);
		service.test();

	}
}

In the above class we are setting the user id in the thread local variable “currentUser”

Now let’s fetch from the variable in service class:

package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestService {

	@Autowired
	private TestRepository repository;

	Logger logger = LoggerFactory.getLogger(TestService.class);

	public void test() {

		logger.info("Current user in Service class " + UserUtil.currentUser.get());

		repository.test();

	}
}

And do the same again in repository class:

package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class TestRepository {

	Logger log = LoggerFactory.getLogger(TestRepository.class);

	public void test() {

		log.info("Current user in Repository class " + UserUtil.currentUser.get());
	}
}

And let’s run the application and hit the test api:

Here is the output from the logs:

We were able to share the user id across different components of the application without passing it in method arguments!

Use cases:

Here are few use cases to use Thread Local variable:

  1. Storing user-specific data in web applications (similar to what we did in the example): In a web application, each incoming request is usually handled by a separate thread. ThreadLocal can be used to store user-specific data, such as the user’s language preference or timezone, that needs to be accessed by multiple parts of the application during the processing of the request.
  2. Managing database connections in multi-threaded applications: In a multi-threaded application, each thread may need to access a database connection. ThreadLocal can be used to ensure that each thread has its own database connection, rather than having to share a connection or synchronize access to a shared connection.
  3. Managing transactional data in multi-threaded applications: In a multi-threaded application that uses transactions, each thread may need to maintain its own transactional state. ThreadLocal can be used to store transactional data for each thread, allowing each thread to maintain its own transactional context.
  4. Storing contextual data in logging frameworks: Logging frameworks, such as Log4j or SLF4J, often use ThreadLocal to store contextual data, such as the user ID or session ID, that is associated with each log message. This allows the contextual data to be included in the log message without having to pass it explicitly to each logging call.

Application frameworks like Spring use ThreadLocal internally in the background for achieving various use cases as above.

Limitations:

Thread Local has certain limitations:

  • It is mutable :

Once you set a thread local variable you can set it again in a different method , this makes it less reliable

  • It has a longer life time

ThreadLocal variable exists in memory as long as the thread executes. You can delete it in your code using ThreadLocal.remove() method . Again the onus lies with the developer here and the developer may forget to remove it. When the number of threads runs into millions this can cause memory issues.

To resolve these issues, Java 20 came up with Scoped Variables, but that is for a separate post.

That’s it!


Posted

in

,

by

Comments

2 responses to “How to share data across multiple components for a single request in a Spring Boot application?”

  1. YETKİN Avatar

    Çok işime yaradı bende bunu nasıl yapacağımı araştırıyorum. Paylaşım için teşekkür ederim.

  2. ALİ Avatar

    Harika bir paylaşım, özellikle konunun önemli detayları oldukça net bir şekilde açıklanmış. İnsanları çeşitli karmaşık anahtar kelimelerle yormak yerine, okumaktan keyif alacağı içerikler her zaman daha iyidir. Kaliteli paylaşım adına teşekkür eder, paylaşımlarınızın devamını sabırsızlıkla beklerim.

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading