繁体   English   中英

如何在具有自定义范围的spring应用程序中以编程方式“发布” bean

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM