簡體   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