上下文 :
我需要使用 Spring 对来自 JMS 的消息应用一个 treament。我对整个过程的最大部分没有任何控制权,但我知道它发生在单个线程中(通常某些信息可以使用 ThreadLocals 获得)。
对于治疗,我称之为服务链。我无法控制这些服务的方法签名或初始化方式,只能在实现上控制。
我需要将信息从链的入口点传递到它的最新步骤。我可以为此使用 ThreadLocal,但我想知道是否有办法使用 Spring 的线程范围来做到这一点。
我能做什么:
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)你必须创建"thread"作用域,它仍然使用下面的ThreadLocal实现。2) 在 applicationContext 中没有 put() 方法。3) 所有处理器(链步骤)都需要自动连接弹簧上下文。
结论:只需使用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。这是我的绒球.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 url,并查看控制台以获取结果。每个线程都有自己的值。