简体   繁体   English

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

[英]How to “publish” programmatically a bean in a spring application with a custom scope

Context : 内容:

I need to apply a treament to a message coming from JMS using Spring. 我需要对使用Spring从JMS发出的消息进行处理。 I don't have any control on the biggest part of the whole process, but I do know that it take place in a single thread (typically some information are avaiable using ThreadLocals). 我没有控制整个过程的大部分,但是我知道它发生在单个线程中(通常使用ThreadLocals可以获得某些信息)。

For the treament , I call a chain of service. 对于处理,我称之为服务链。 I don't have any control on the methods's signature of these services, or how there are initialized, only on the implementation. 我对这些服务的方法签名或如何进行初始化没有任何控制,仅对实现有任何控制。

I need to pass down information from the entry point of the chain to the latest step of it. 我需要将信息从链的入口点传递到它的最新步骤。 I could use for this a ThreadLocal, but I was wondering is there is way to do it using the Thread Scope of Spring. 我可以为此使用ThreadLocal,但是我想知道是否可以使用Spring的Thread Scope来做到这一点。

What I could do : 我能做什么:

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
    }
}

What I would like to do (sort of): 我想做的(某种):

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
    }
}

I don't think there is any benefit in using a Spring bean for this. 我认为为此使用Spring bean没有任何好处。

1) You would have to create the "thread" scope, which would still be using the ThreadLocal implementation underneath. 1)您将必须创建“线程”作用域,该作用域仍将使用下面的ThreadLocal实现。 2) There is no put() method in applicationContext. 2)applicationContext中没有put()方法。 3) All processors (chain steps) would need to autowire the spring context. 3)所有处理器(链式步骤)都需要自动连接spring上下文。

Conclusion: just use the ThreadLocal but don't forget to clean it up when you're done processing. 结论:仅使用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();
        }
    }
}

Having said that, here is a working example of using the SimpleThreadScope of Spring: 话虽如此,这是使用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);
        }

    }
}

I'm using Spring Boot 1.3.3. 我正在使用Spring Boot 1.3.3。 This is in my pom.xml 这是在我的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>

To test this hit the http://localhost:8080/test url multiple times within 10 seconds and see the console for the results. 要测试此结果,请在10秒内多次击中http:// localhost:8080 / test网址,并在控制台中查看结果。 Each thread has its own value. 每个线程都有其自己的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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