I'm connecting to PubNub in a Spring Boot application. From the documentation, it's ok to re-use PubNub objects but it's better to have one per thread. What's the appropriate method to store and retrieve one object per thread in Spring Boot?
This is how you'd store and retrieve an object per thread in Spring using ThreadLocal
, this example is based on Spring's own ThreadLocalSecurityContextHolderStrategy which is used to store SecurityContext
per thread.
Also, take a look at InheritableThreadLocal especially if your code spins up new thread, e.g. Spring's @Async
annotation, it has mechanisms to propagate existing or create new thread local values when creating child threads.
import org.springframework.util.Assert;
final class ThreadLocalPubNubHolder {
private static final ThreadLocal<PubNub> contextHolder = new ThreadLocal<PubNub>();
public void clearContext() {
contextHolder.remove();
}
public PubNub getContext() {
PubNub ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(PubNub context) {
Assert.notNull(context, "Only non-null PubNub instances are permitted");
contextHolder.set(context);
}
public PubNub createEmptyContext() {
// TODO - insert code for creating a new PubNub object here
return new PubNubImpl();
}
}
You can use Java ThreadLocal support as mentioned above by @SergeyB. Another way to do it is to use Thread Scope for your beans:
@Configuration
public class AppConfig {
//Register thread scope for your application
@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
Then you can create a bean with a thread scope (proxy mode will be explained below):
@Scope(value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class PubSubContext {
private PubSub pubSub;
public PubSub getPubSub() {
return pubSub;
}
public void setPubSub(PubSub pubSub) {
this.pubSub = pubSub;
}
@PostConstruct
private void init() {
// TODO: your code for initializing PubSub object
log.info("RequiredMessageHeaders started in thread " + Thread.currentThread().getId());
}
@PreDestroy
private void destroy() {
// TODO: your code for cleaning resources if needed
log.info("RequiredMessageHeaders destroyed in thread " + Thread.currentThread().getId());
}
}
The last step is to inject PubSubContext
where you need it:
@Controller
public class YourController {
// Spring will inject here different objects specific for each thread.
// Note that because we marked PubSubContext with proxyMode = ScopedProxyMode.TARGET_CLASS we do not need to use applicationContext.get(PubSubContext.class) to obtain a new bean for each thread - it will be handled by Spring automatically.
@Autowired
private PubSubContext pubSubContext;
@GetMapping
public String yourMethod(){
...
PubSub pubSub = pubSubContext.getPubSub();
...
}
}
With this approach, you could go even further and mark your PubSubContext as @Lazy, so it won't be created until it's requested inside yourMethod
:
@Controller
public class YourController {
@Lazy
@Autowired
private PubSubContext pubSubContext;
...
}
As you see PubSubContext
does basically what ThreadLocal does but leveraged by Spring capabilities.
Hope it helps!
First of all,
As it is safe to use single PubNub object in multiple threads,
You need multiple PubNub objects ONLY if you need performance increase
If that is your case - my suggestion will be to organize pool of PubNub objects (the use case is quite close to DB connection use case).
© 2022 - 2024 — McMap. All rights reserved.