如何在具有自定义作用域的弹簧应用程序中以编程方式“发布”bean
问题描述:
上下文:如何在具有自定义作用域的弹簧应用程序中以编程方式“发布”bean
我需要使用Spring对来自JMS的消息进行处理。我对整个流程的最大部分没有任何控制权,但是我确实知道它发生在单个线程中(通常一些信息可以使用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)您将不得不创建“线程”范围,该范围仍将使用下面的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 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网址多次,看到控制台的结果。每个线程都有自己的价值。