春季 - 在运行时寄存器作用域的 Bean

2022-09-03 15:47:40

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

任务在运行时中实例化。它以对象的形式提供一些配置。我想将该对象注册到任务范围内的 but 中,以便该范围内的所有 Bean 都可以引用该特定任务的配置。PropertiesApplicationContext

下面是代码中的粗略想法:

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

我不知道如何在适当作用域内的bean中注册bean。ApplicationContext

谢谢

更新:

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

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

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

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);
        // ...
    }
}

答案 1

如果我接受您的最后一条评论:

我想要的是在每个作用域(在每个MyTask中)有1个SomeScopedBean实例,但每个实例都配置了不同的配置属性(这些属性由部署框架在实例化每个任务时提供,

特别是如果它被限制在.within each MyTaskMyTask

您可以:

  • 定义为原型豆SomeScopedBean
  • 创建一个工厂,该工厂将使用提供的属性配置进行实例化@ConfigurationSomeScopedBean

首先是配置:

@Configuration
public class SomeScopedBeanFactoryConfiguration {

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

}

然后自动将 连接到 并创建:SomeScopedBeanFactoryConfigurationMyTaskSomeScopedBean

public class MyTask extends SourceTask {

    @Autowired
    private SomeScopedBeanFactoryConfiguration  someScopedBeanFactoryConfiguration;

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

注意:如果必须使用/ scope注入多个bean,则可以将其范围更改为线程范围1,例如:SomeScopedBeantaskthread

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

答案 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 存储中)和 on call 委托逻辑,以从 Map 中更正一个实例。

更新:实际上,您自动连接不是MyTask Bean,而是代理。代理将包装所有 MyTask 方法。代理具有与 MyTask 相同的接口。假设您调用 ProxyMyTask.do() 方法。代理拦截调用,以某种方式获取范围,例如,在Tread Scope示例的情况下获取当前线程,并从Map(或ThreadLocal存储中的线程范围)获取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);
}

推荐