Spring - 在运行时注册scoped bean

时间:2017-04-12 09:20:20

标签: java spring

我正在开发一个基于Spring的应用程序,它注册了一个自定义范围"任务" 。我们的想法是,当一个新任务启动时,Spring应该提供任务范围的对象。

该任务在运行时中实例化。它以Properties对象的形式提供了一些配置。我想用ApplicationContext注册该对象,但是在任务范围内,以便该范围内的所有bean都可以引用该特定任务的配置。

以下是代码中的粗略概念:

public class MyTask extends SourceTask {
    @Override
    public void start(Map<String, String> props) {
        context = ContextProvider.getApplicationContext();
        // Initialize the scope
        ConnectorTaskScope scope = context.getBean(ConnectorTaskScope.class);
        scope.startNewTask();

        // TODO register the props object in the context

        // get an object which requires the properties and work with it
        context.getBean(SomeScopedBean.class);        
    }
}

我无法弄清楚如何在适当范围内的ApplicationContext中注册一个bean。

谢谢

更新

这里有一些代码可以更好地解释这个问题。 SomeScopedBean应该使用bean提供的配置做一些事情,看起来像这样:

public class SomeScopedBean {
    @Autowire
    public SomeScopedBean (Properties configuration) {
        // do some work with the configuration 
    }
}

应用程序的想法是它应该有多个MyTask实例以不同的配置运行,每个任务都是自己的范围。在每个任务的范围内,应该有1个SomeScopedBean实例使用任务的配置进行初始化。

public class MyApplication {
    public static void main (String[] args) {
        // ...
        Properties config1 = loadConfiguration1();
        Properties config2 = loadConfiguration2();
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();
        task1.start(config1);
        task2.start(config2);
        // ...
    }
}

2 个答案:

答案 0 :(得分:3)

如果我接受你的最后评论:

  

我想要的是在每个范围内有一个SomeScopedBean实例   (在每个MyTask中),但每个配置有不同的配置   属性(由部署框架提供时   实例化每个任务,

尤其是within each MyTask,如果仅限于MyTask

你可以:

  • SomeScopedBean定义为原型bean
  • 创建一个工厂@Configuration,它将使用提供的属性配置实例化SomeScopedBean

首先是配置:

@Configuration
public class SomeScopedBeanFactoryConfiguration {

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public SomeScopedBean create(Properties configuration) {
        return new SomeScopedBean(configuration);
    }

}

然后将SomeScopedBeanFactoryConfiguration自动装入MyTask并创建SomeScopedBean

public class MyTask extends SourceTask {

    @Autowired
    private SomeScopedBeanFactoryConfiguration  someScopedBeanFactoryConfiguration;

    @Override
    public void start(Map<String, String> props) {
        SomeScopedBean scopedBean = someScopedBeanFactoryConfiguration.create(props);    
    }
}

注意:如果必须在SomeScopedBean / task范围内的多个bean中注入thread,则可以将其范围更改为您的线程范围,例如:

    @Bean
    @Scope("thread")
    public SomeScopedBean create(Properties configuration) {
        return new SomeScopedBean(configuration);
    }

答案 1 :(得分:2)

AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();

MyTask instance = new MyTask();
beanFactory.autowireBean(instance);
beanFactory.initializeBean(instance, MyTask.class.getCanonicalName());

//for singleton I used
((ConfigurableListableBeanFactory)beanFactory).registerSingleton(MyTask.class.getCanonicalName(), instance);

在你的情况下,我会注册MyTask Proxy的单例。代理可以保留所有与范围相关的实例(例如在Map或ThreadLocal存储中)和调用委托逻辑以从Map中更正一个。

更新: 实际上你不是自动装配MyTask bean而是代理。代理包装了所有MyTask方法。 Proxy具有与MyTask相同的接口。假设您调用ProxyMyTask.do()方法。代理拦截呼叫,获得某种范围,例如在Tread Scope示例的情况下的当前线程并从Map(或ThreadLocal存储的Thread Scope)获取MyTask的正确实例。最后调用找到的MyTask实例的do(0方法。

更新2: 请参阅示例http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html您可以轻松地包装接口。您确定范围和返回正确实例的逻辑应该在方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return method.invoke(target, args);
}