当前位置: 首页 > 知识库问答 >
问题:

如何在运行时实例化Spring托管bean?

董子平
2023-03-14

我坚持从普通Java到Spring的简单重构。应用程序有一个“容器”对象,该对象在运行时实例化其部分。让我用代码解释一下:

public class Container {
    private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();

    public void load() {
        // repeated several times depending on external data/environment
        RuntimeBean beanRuntime = createRuntimeBean();
        runtimeBeans.add(beanRuntime);
    }

    public RuntimeBean createRuntimeBean() {
         // should create bean which internally can have some 
         // spring annotations or in other words
         // should be managed by spring
    }
}

基本上,在加载容器期间,要求一些外部系统向他提供关于每个RuntimeBean的数量和配置的信息,然后它根据给定的规范创建bean。

问题是:通常当我们在Spring这样做的时候

ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");

我们的对象已完全配置,并已注入所有依赖项。但是在我的例子中,我必须实例化一些对象,在我执行load()方法后,这些对象也需要依赖注入。
我如何实现这一点?

我使用的是基于Java的配置。我已经尝试为RuntimeBeans创建工厂了

public class BeanRuntimeFactory {

    @Bean
    public RuntimeBean createRuntimeBean() {
        return new RuntimeBean();
    }
}

期望@Bean在所谓的“lite”模式下工作。http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html不幸的是,我发现只做新的RuntimeBean()没有什么区别;这里有一篇文章有一个类似的问题:如何管理FactoryBean spring创建的bean?

还有http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html但就我而言,它看起来像一把锤子。

我还尝试了ApplicationContext。getBean(“runtimeBean”,args),其中runtimeBean有一个“原型”范围,但getBean是一个糟糕的解决方案。

为了更具体,我试图重构这个类:https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java@see#load()方法,并找到返回创建(cd,false);

我在spring文档中发现了一个非常有趣的叫做“查找方法注入”的东西:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-工厂查找法注入

还有一张有趣的jira票https://jira.spring.io/browse/SPR-5192菲尔·韦伯说https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051

还有http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html

所有这些“lookup”方法的问题是它们不支持传递任何参数。。我还需要像使用applicationContext一样传递参数。getBean(“runtimeBean”,arg1,arg2)。看起来它是在某个地方用https://jira.spring.io/browse/SPR-7431

GoogleGuice有一个整洁的特性,叫做AssistedInject。https://github.com/google/guice/wiki/AssistedInject

共有3个答案

解明辉
2023-03-14

您不需要Container,因为所有运行时对象都应该由Application ationContext创建、持有和管理。想想一个Web应用程序,它们几乎是一样的。每个请求都包含您上面提到的外部数据/环境信息。您需要的是一个原型/请求范围的bean,比如外部数据或环境信息,它可以通过静态方式读取和保存运行时数据,比方说静态工厂方法。

<bean id="externalData" class="ExternalData"
    factory-method="read" scope="prototype"></bean>

<bean id="environmentInfo" class="EnvironmentInfo"
    factory-method="read" scope="prototype/singleton"></bean>

<bean class="RuntimeBean" scope="prototype">
    <property name="externalData" ref="externalData">
    <property name="environmentInfo" ref="environmentInfo">
</bean> 

如果您确实需要一个容器来保存运行时对象,那么代码应该是

class Container {

    List list;
    ApplicationContext context;//injected by spring if Container is not a prototype bean

    public void load() {// no loop inside, each time call load() will load a runtime object
        RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
        list.add(bean);// do whatever
    }
}

带有原型bean依赖项的官方doc单例bean。

何安宜
2023-03-14

我认为您使用RuntimeBean beanRuntime=createRuntimeBean()的概念是错误的
您绕过了Spring容器,转而使用常规java构造函数,因此工厂方法上的任何注释都将被忽略,并且Spring永远不会管理这个bean

这是一个在一个方法中创建多个原型bean的解决方案,看起来不太好看,但应该可以工作,我在RuntimeBean中自动连接了容器,作为日志中显示的自动连接的证明。您也可以在日志中看到,当您运行这个方法时,每个bean都是原型的新实例。

'

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

        ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
        Container container = (Container) context.getBean("container");
        container.load();
    }
}

@Component
class Container {
    private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
    @Autowired
    ApplicationContext context;

    @Autowired
    private ObjectFactory<RuntimeBean> myBeanFactory;

    public void load() {

        // repeated several times depending on external data/environment
        for (int i = 0; i < 10; i++) {
            // **************************************
            // COMENTED OUT THE WRONG STUFFF 
            // RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
            // createRuntimeBean();
            // 
            // **************************************

            RuntimeBean beanRuntime = myBeanFactory.getObject();
            runtimeBeans.add(beanRuntime);
            System.out.println(beanRuntime + "  " + beanRuntime.container);
        }
    }

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public RuntimeBean createRuntimeBean() {
        return new RuntimeBean();
    }
}

// @Component

class RuntimeBean {
    @Autowired
    Container container;

} '
郎建章
2023-03-14

看来我找到了解决办法。因为我使用的是基于java的配置,所以比您想象的还要简单。xml中的另一种方法是查找方法,但仅适用于SpringVersion4.1。X,因为它支持向方法传递参数。

下面是一个完整的工作示例:

public class Container {
    private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
    private RuntimeBeanFactory runtimeBeanFactory;

    public void load() {
        // repeated several times depending on external data/environment
        runtimeBeans.add(createRuntimeBean("Some external info1"));
        runtimeBeans.add(createRuntimeBean("Some external info2"));
    }

    public RuntimeBean createRuntimeBean(String info) {
         // should create bean which internally can have some 
         // spring annotations or in other words
         // should be managed by spring
         return runtimeBeanFactory.createRuntimeBean(info);
    }

    public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
        this.runtimeBeanFactory = runtimeBeanFactory;
    }
}

public interface RuntimeBeanFactory {
    RuntimeBean createRuntimeBean(String info);
}

//and finally
@Configuration
public class ApplicationConfiguration {
    
    @Bean
    Container container() {
        Container container = new Container(beanToInject());
        container.setBeanRuntimeFactory(runtimeBeanFactory());
        return container;
    }
        
    // LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
    @Bean 
    public BeanRuntimeFactory runtimeBeanFactory() {
        return new BeanRuntimeFactory() {
            public RuntimeBean createRuntimeBean(String beanName) {
                return runtimeBean(beanName);
            }
        };
    }
    
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    RuntimeBean runtimeBean(String beanName) {
        return new RuntimeBean(beanName);
    }
}

class RuntimeBean {
    @Autowired
    Container container;
}

就这样。

谢谢大家。

 类似资料:
  • 问题内容: 我坚持了从纯Java到Spring的简单重构。应用程序具有一个“容器”对象,该对象在运行时实例化其部分。让我用代码解释一下: 基本上,在装入容器时,它要求某个外部系统向他提供有关每个RuntimeBean的数量和配置的信息,然后根据给定的规范创建bean。 问题是:通常在春季时 我们的对象已完全配置,并注入了所有依赖项。但就我而言,我必须实例化一些对象,这些对象在执行load()方法后

  • 我使用JSF和托管Beans创建了一个简单的应用程序。从托管bean,我试图实例化一个将数据存储到数据库的spring bean。然而,“@autowired”注释似乎不起作用,因为我得到了一个nullpointerexcpetion。我阅读了关于如何在Spring中使用JSF和托管bean的所有相关主题,但不能解决这个问题。也许有人可以看看下面的代码,给我一个提示? 服务Bean faces.c

  • 问题内容: 因此,标题非常简单。我有一个处理程序类,它实现了spring提供的接口。在这个类中,我将添加多个具有如下bean类设置的bean : 该方法返回的对象。 稍后,我将获取类实现的必需实例。所有这些都很好。当我要删除其中一个实例并在以后没有注册表实例的位置添加新实例时,就会出现问题。谁能帮我找到一种方法吗? 以下是该类的代码- 问题答案: 您可以利用(在此处查找API)动态删除或注册Bea

  • 问题内容: 我创建了一个Spring方面来处理Retry机制。我还创建了一个Retry注释。以下是重试注释的代码以及处理此注释的方面。 要启用注释,我需要实例化RetryInterceptor类。我要确保对于给定的上下文,只有该对象的一个​​实例。如果由于某种原因创建了多个对象,那么我的建议将被应用多次。我如何才能完全确保始终有1个实例? 问题答案: 我找到了一种方法:)引用:超越DI 我在我的根

  • 注意:这个问题可能与Vaadin有关,也可能与Vaadin无关,这取决于是否有“更好的”解决方案来“重置”bean。 背景场景 我正在构建一个用于输入一些值的向导,当这些值完成时,将发送到一个表(使用Vaadin和加载项“Wizards for Vaadin”)。 该加载项没有提供一种方法来重置向导(即返回到步骤1)而不强制调用当前steps(重写)onAdvance()和onBack()方法,这