我在Spring Boot应用程序中连接到PubNub。 From the documentation, it's ok to re-use PubNub objects但每个帖子最好有一个。在Spring Boot中每个线程存储和检索一个对象的适当方法是什么?
答案 0 :(得分:7)
这是你使用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();
}
}
答案 1 :(得分:3)
您可以使用@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功能正在利用它。
希望它有所帮助!
答案 2 :(得分:0)
首先,
因为在多个线程中使用单个PubNub对象是安全的,
如果您需要提高性能,则只需要多个PubNub对象
如果是这种情况 - 我的建议是组织pool PubNub对象(用例非常接近数据库连接用例)。
答案 3 :(得分:-1)
我假设当你谈到正在生成的新线程时,我们正在处理每个输入请求到tomcat服务器的连接到PubNub。
我认为处理这种情况的一个好方法是为每个请求创建一个新的PubNub对象,让spring处理对象的生命周期。
使用原型范围的下面的bean定义允许为应用程序上下文中的每个查找创建新的PubNub对象。
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
PubNub getPubNub() {
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.setSubscribeKey("SubscribeKey");
pnConfiguration.setPublishKey("PublishKey");
pnConfiguration.setSecure(false);
PubNub pubnub = new PubNub(pnConfiguration);
return pubnub;
}
您希望使用上面创建的PubNub对象的服务类如下所示:
class PubNubService{
@Autowired
ApplicationContext context;
...
public void process()
{
PubNub pubNub = context.getBean(PubNub.class);
pubNub.publish()....
//your code for publish goes here
}
}
我希望我对你想要解决的场景/用例的假设是正确的。如果假设错误/需要纠正,请向我们提供更多信息。请告诉我们它是如何发展的。