如何在 Spring 应用程序中拥有每线程但可重用的对象 (PubNub)?
How to have per-thread but reusable objects (PubNub) in a Spring app?
我正在 Spring 引导应用程序中连接到 PubNub。 From the documentation, it's ok to re-use PubNub objects 但最好每个线程有一个。 Spring Boot 中每个线程存储和检索一个对象的合适方法是什么?
这就是使用 ThreadLocal
在 Spring 中每个线程存储和检索对象的方式,此示例基于 Spring 自己的 ThreadLocalSecurityContextHolderStrategy用于每个线程存储SecurityContext
。
此外,请查看 InheritableThreadLocal,尤其是当您的代码启动新线程时,例如Spring 的 @Async
注释,它具有在创建子线程时传播现有或创建新线程局部值的机制。
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();
}
}
您可以使用@SergeyB 上面提到的 Java ThreadLocal 支持。另一种方法是为您的 bean 使用 Thread Scope:
@Configuration
public class AppConfig {
//Register thread scope for your application
@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
然后就可以创建线程作用域的bean了(下面会解释代理模式):
@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());
}
}
最后一步是在你需要的地方注入PubSubContext
:
@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();
...
}
}
使用这种方法,您可以更进一步,将您的 PubSubContext 标记为 @Lazy,这样在 yourMethod
内部请求之前不会创建它:
@Controller
public class YourController {
@Lazy
@Autowired
private PubSubContext pubSubContext;
...
}
如您所见,PubSubContext
基本上完成了 ThreadLocal 所做的工作,但利用了 Spring 功能。
希望对您有所帮助!
首先,
因为在多个线程中使用单个 PubNub 对象是安全的,
仅当您需要提高性能时才需要多个 PubNub 对象
如果是这种情况 - 我的建议是组织 pool 个 PubNub 对象(用例非常接近数据库连接用例)。
我正在 Spring 引导应用程序中连接到 PubNub。 From the documentation, it's ok to re-use PubNub objects 但最好每个线程有一个。 Spring Boot 中每个线程存储和检索一个对象的合适方法是什么?
这就是使用 ThreadLocal
在 Spring 中每个线程存储和检索对象的方式,此示例基于 Spring 自己的 ThreadLocalSecurityContextHolderStrategy用于每个线程存储SecurityContext
。
此外,请查看 InheritableThreadLocal,尤其是当您的代码启动新线程时,例如Spring 的 @Async
注释,它具有在创建子线程时传播现有或创建新线程局部值的机制。
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();
}
}
您可以使用@SergeyB 上面提到的 Java ThreadLocal 支持。另一种方法是为您的 bean 使用 Thread Scope:
@Configuration
public class AppConfig {
//Register thread scope for your application
@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
然后就可以创建线程作用域的bean了(下面会解释代理模式):
@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());
}
}
最后一步是在你需要的地方注入PubSubContext
:
@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();
...
}
}
使用这种方法,您可以更进一步,将您的 PubSubContext 标记为 @Lazy,这样在 yourMethod
内部请求之前不会创建它:
@Controller
public class YourController {
@Lazy
@Autowired
private PubSubContext pubSubContext;
...
}
如您所见,PubSubContext
基本上完成了 ThreadLocal 所做的工作,但利用了 Spring 功能。
希望对您有所帮助!
首先,
因为在多个线程中使用单个 PubNub 对象是安全的,
仅当您需要提高性能时才需要多个 PubNub 对象
如果是这种情况 - 我的建议是组织 pool 个 PubNub 对象(用例非常接近数据库连接用例)。