如何在具有自定义作用域的 Spring 应用程序中以编程方式"publish" Bean



上下文 :

我需要使用 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,并查看控制台以获取结果。每个线程都有自己的值。

最新更新