如何在具有自定义作用域的弹簧应用程序中以编程方式“发布”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网址多次,看到控制台的结果。每个线程都有自己的价值。