[英]How to “publish” programmatically a bean in a spring application with a custom scope
內容:
我需要對使用Spring從JMS發出的消息進行處理。 我沒有控制整個過程的大部分,但是我知道它發生在單個線程中(通常使用ThreadLocals可以獲得某些信息)。
對於處理,我稱之為服務鏈。 我對這些服務的方法簽名或如何進行初始化沒有任何控制,僅對實現有任何控制。
我需要將信息從鏈的入口點傳遞到它的最新步驟。 我可以為此使用ThreadLocal,但是我想知道是否可以使用Spring的Thread Scope來做到這一點。
我能做什么:
public class ThreadHolder {
private static final ThreadLocal<Object> objectThreadLocal = new ThreadLocal<>();
public static Object getObject() {
return objectThreadLocal.get();
}
public static void setObject(Object object) {
objectThreadLocal.set(object);
}
public static void cleanObject() {
objectThreadLocal.remove();
}
}
public class MyController {
public MandatoryInsurance process(JMSMessage message) {
Object valueToPassDown = retrieveReferenceValueCode(); //Object here, but could be a String if easier
ThreadHolder.setObject(valueToPassDown);
TreamentResult treamentResult = chainEntryPoint.startTreament(message); //The chainEntryPoint will call chainStep.executeTreamentStep)
return treatmentResult;
}
}
public class ChainStep {
public TreamentResult executeTreatmentStep(JMSMessage message) {
Object valuePassedDown = ThreadHolder.getObject()
// Do treament
}
}
我想做的(某種):
public class MyController {
@Autowired
ApplicationContext context;
public MandatoryInsurance process(JMSMessage message) {
Object valueToPassDown = retrieveReferenceValueCode(); //Object here, but could be a String if easier
context.put("valueToPassDown", valueToPassDown, "thread");
TreamentResult treamentResult = chainEntryPoint.startTreament(message); //The chainEntryPoint will call chainStep.executeTreamentStep)
ThreadHolder.cleanOject();
return treatmentResult;
}
}
public class ChainStep {
public TreamentResult executeTreatmentStep(JMSMessage message) {
Object valuePassedDown = context.getBean("valueToPassDown");
// Do treament
}
}
我認為為此使用Spring bean沒有任何好處。
1)您將必須創建“線程”作用域,該作用域仍將使用下面的ThreadLocal實現。 2)applicationContext中沒有put()方法。 3)所有處理器(鏈式步驟)都需要自動連接spring上下文。
結論:僅使用ThreadLocal,但不要忘記在完成處理后將其清理。
public class MyController {
public MandatoryInsurance process(JMSMessage message) {
Object valueToPassDown = retrieveReferenceValueCode(); //Object here, but could be a String if easier
try {
ThreadHolder.setObject(valueToPassDown);
TreamentResult treamentResult = chainEntryPoint.startTreament(message); //The chainEntryPoint will call chainStep.executeTreamentStep)
return treatmentResult;
} finally {
ThreadHolder.cleanOject();
}
}
}
話雖如此,這是使用Spring的SimpleThreadScope的一個有效示例:
package com.test.boot;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.SimpleThreadScope;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder(App.class).build().run(args);
}
@Bean
public CustomScopeConfigurer customScope() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
Map<String, Object> threadScope = new HashMap<String, Object>();
threadScope.put("thread", new SimpleThreadScope());
configurer.setScopes(threadScope);
return configurer;
}
@Component
@Scope("thread")
public static class MyThreadLocal {
String value;
}
@RestController
public static class Controller {
@Autowired
ApplicationContext appContext;
@Autowired
ChainStep chainStep;
@RequestMapping(value = "/test")
public String process() throws InterruptedException {
MyThreadLocal bean = appContext.getBean(MyThreadLocal.class);
bean.value = "" + Math.random();
System.out.println(Thread.currentThread().getName() + " begin processing, value=" + bean.value);
chainStep.executeStep();
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " end processing, value=" + bean.value);
return "ok";
}
}
@Component
public static class ChainStep {
@Autowired
ApplicationContext appContext;
public void executeStep() throws InterruptedException {
MyThreadLocal bean = appContext.getBean(MyThreadLocal.class);
System.out.println(Thread.currentThread().getName() + " middle processing, value=" + bean.value);
}
}
}
我正在使用Spring Boot 1.3.3。 這是在我的pom.xml中
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
要測試此結果,請在10秒內多次擊中http:// localhost:8080 / test網址,並在控制台中查看結果。 每個線程都有其自己的值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.