以编程方式将 Bean 添加到 Spring Web App 上下文

Posted

技术标签:

【中文标题】以编程方式将 Bean 添加到 Spring Web App 上下文【英文标题】:Add Bean Programmatically to Spring Web App Context 【发布时间】:2011-05-31 06:07:24 【问题描述】:

由于是插件架构,我正在尝试以编程方式将 bean 添加到我的 web 应用程序中。我有一个通过@Component 注释创建的Spring bean,我正在实现ApplicationContextAware 接口。

我的覆盖函数如下所示:

@Override
public void setApplicationContext(ApplicationContext applicationContext)
        throws BeansException 

    // this fails
    this.applicationContext = (GenericWebApplicationContext) applicationContext;
 

基本上,我不知道如何将 bean 添加到给 setApplicationContext 的 applicationContext 对象。谁能告诉我我是怎么搞错的?

好的,这就是我最终得到的解决方案:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry bdr)
        throws BeansException 
    BeanDefinition definition = new RootBeanDefinition(
            <My Class>.class);

    bdr.registerBeanDefinition("<my id>", definition);

【问题讨论】:

【参考方案1】:

首先初始化属性值

MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.add("hostName", details.getHostName());
mutablePropertyValues.add("port", details.getPort());

DefaultListableBeanFactory context = new DefaultListableBeanFactory();
GenericBeanDefinition connectionFactory = new GenericBeanDefinition();
connectionFactory.setBeanClass(Class);
connectionFactory.setPropertyValues(mutablePropertyValues);

context.registerBeanDefinition("beanName", connectionFactory);

添加到 bean 列表

ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton("beanName", context.getBean("beanName"));

【讨论】:

【参考方案2】:

为什么需要GenericWebApplicationContext 类型? 我认为您可能可以使用任何 ApplicationContext 类型。

通常你会使用一个 init 方法(除了你的 setter 方法):

@PostConstruct
public void init()
    AutowireCapableBeanFactory bf = this.applicationContext
        .getAutowireCapableBeanFactory();
    // wire stuff here

您可以使用任一方法连接 bean

AutowireCapableBeanFactory.autowire(Class, int mode, boolean dependencyInject)

AutowireCapableBeanFactory.initializeBean(Object existingbean, String beanName)

【讨论】:

当我这样做时,它不会将它们添加到 ApplicationContext【参考方案3】:

实际上AnnotationConfigApplicationContext 派生自AbstractApplicationContext,其中有空的postProcessBeanFactory 方法可供覆盖

/**
 * Modify the application context's internal bean factory after its standard
 * initialization. All bean definitions will have been loaded, but no beans
 * will have been instantiated yet. This allows for registering special
 * BeanPostProcessors etc in certain ApplicationContext implementations.
 * @param beanFactory the bean factory used by the application context
 */
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 

要利用这一点,请创建如下所示的 AnnotationConfigApplicationContextProvider 类(给定 Vertx 实例示例,您可以改用 MyClass)...

public class CustomAnnotationApplicationContextProvider 
private final Vertx vertx;

public CustomAnnotationApplicationContextProvider(Vertx vertx) 
    this.vertx = vertx;


/**
 * Register all beans to spring bean factory
 *
 * @param beanFactory, spring bean factory to register your instances
 */
private void configureBeans(ConfigurableListableBeanFactory beanFactory) 
    beanFactory.registerSingleton("vertx", vertx);


/**
 * Proxy method to create @link AnnotationConfigApplicationContext instance with no params
 *
 * @return @link AnnotationConfigApplicationContext instance
 */
public AnnotationConfigApplicationContext get() 
    return new AnnotationConfigApplicationContext() 

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        
    ;


/**
 * Proxy method to call @link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory) with our logic
 *
 * @param beanFactory bean factory for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory)
 */
public AnnotationConfigApplicationContext get(DefaultListableBeanFactory beanFactory) 
    return new AnnotationConfigApplicationContext(beanFactory) 

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        
    ;


/**
 * Proxy method to call @link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[]) with our logic
 *
 * @param annotatedClasses, set of annotated classes for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[])
 */
public AnnotationConfigApplicationContext get(Class<?>... annotatedClasses) 
    return new AnnotationConfigApplicationContext(annotatedClasses) 

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        
    ;


/**
 * proxy method to call @link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) with our logic
 *
 * @param basePackages set of base packages for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)
 */
public AnnotationConfigApplicationContext get(String... basePackages) 
    return new AnnotationConfigApplicationContext(basePackages) 

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        
    ;


在创建ApplicationContext 时,您可以使用

Vertx vertx = ...; // either create or for vertx, it'll be passed to main verticle
ApplicationContext context = new CustomAnnotationApplicationContextProvider(vertx).get(ApplicationSpringConfig.class);

【讨论】:

【参考方案4】:

这是一个简单的代码:

ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton(bean.getClass().getCanonicalName(), bean);

【讨论】:

你能详细说明答案吗?【参考方案5】:

在 Spring 3.0 中,您可以使您的 bean 实现 BeanDefinitionRegistryPostProcessor 并通过 BeanDefinitionRegistry 添加新 bean。

在以前的 Spring 版本中,您可以在 BeanFactoryPostProcessor 中执行相同的操作(尽管您需要将 BeanFactory 强制转换为 BeanDefinitionRegistry,这可能会失败)。

【讨论】:

谢谢。我想最难的部分是知道要寻找什么。 请注意JavaDoc: A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead. BeanDefinitionRegistryPostProcessor 怎么办?假设我写了这个类。接下来是什么?如何激活它?

以上是关于以编程方式将 Bean 添加到 Spring Web App 上下文的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式将“@Cascade”添加到 POJO 字段(Spring mvc + Hibernate)

如何以编程/动态方式将组件添加到 p:dataTable facet

可以在加载的 Spring 上下文中以编程方式替换 Spring Bean

如何以编程方式注册 JSF 托管 bean?

使用客户端凭据流以编程方式在 Azure AD 中添加应用程序

如何使用 Spring Boot 以编程方式确定当前的活动配置文件 [重复]