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

在Spring运行时注册bean(原型)

归俊捷
2023-03-14

只需要社区评估的东西。下面是一段代码,这是一个创建特定类型实例的简单工厂。该方法将在上下文中注册bean作为原型并返回实例。这是我第一次在运行时配置bean。你能好心评估并提供反馈吗?事先谢谢你。

package au.com.flexcontacts.flexoperations;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;

import au.com.flexcontacts.exceptions.SyncClassCreactionError;

/**
 * @author khushroo.mistry
 * Class purpose: Simple Factory to create an 
 * instance of SynchroniseContactsService and register it in the Spring IoC.
 */
public final class FLEXSyncFactory implements ApplicationContextAware {

    private static AbstractApplicationContext context;


    /**
     * @param username
     * @param password
     * @param syncType
     * @return the correct service class
     * @throws SyncClassCreactionError
     * The method registers the classes dynamically into the Spring IoC
     */
    public final SynchroniseContactsService createSyncService(String username, String password, SyncType syncType) throws SyncClassCreactionError {

        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();

        try {

            //Register the bean in the IoC
            BeanDefinition bdb = new GenericBeanDefinition();
            bdb.setBeanClassName(syncType.getClassName());
            bdb.setScope("prototype");
            ConstructorArgumentValues constructor = bdb.getConstructorArgumentValues();
            constructor.addIndexedArgumentValue(0, username);
            constructor.addIndexedArgumentValue(1, password);
            beanFactory.registerBeanDefinition(syncType.getInstanceName(), bdb);

            //Return instance of bean
            return (SynchroniseContactsService) beanFactory.getBean(syncType.getInstanceName());
        } catch (Exception e) {
            e.printStackTrace();
            throw new SyncClassCreactionError("Error: Illegal Handler");
        }

    }

    public void setApplicationContext(ApplicationContext applicationContext)
    throws BeansException {
        context = (AbstractApplicationContext) applicationContext;

    }

}

FLEX Sync工厂已在IoC容器中配置为单例。因此,要创建一个新的同步管理器,我需要执行以下操作:

flexSyncFactory.createSyncService(userName, password, SyncType.FULL);

我使用的是Spring 3.1。请回顾并提供宝贵的反馈。

亲切的问候。

共有3个答案

景轶
2023-03-14

你的解决方案看起来不错。我相信我们也可以通过创建一个bean来实现BeanNameAware和FactoryBean接口,然后在创建上下文之前设置值。

xxxxBean.beansByName.put("synTable", synTable);
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
assert externalDataSource == context.getBean("synTable");

下面是Bean实现

public class xxxxBean implements BeanNameAware, FactoryBean {

    public static Map<String, Object> beans = new HashMap<String, Object>();

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public Object getObject() {
        return beans.get(beanName);
    }

    @Override
    public Class<?> getObjectType() {
        return beans.get(beanName).getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}
奚瑾瑜
2023-03-14

这对我很有用:http://random-thoughts-vortex.blogspot.com/2009/03/create-dynamically-spring-beans.html

声明一个专用的Spring上下文bean,它将实现ApplicationContextAware和BeanFactoryPostProcessor接口:

  public class MyContextWrapper implements ApplicationContextAware,
             BeanFactoryPostProcessor {

   private ApplicationContext appContext;
   private ConfigurableListableBeanFactory factory;

   public void postProcessBeanFactory(ConfigurableListableBeanFactory factory)
              throws BeansException {
   this.factory = factory;
   }
   public void setApplicationContext(ApplicationContext c)
            throws BeansException {
   this.appContext = c;   
   }

   //setters and getters

}

通过在XML配置文件中声明bean,让spring将这个bean加载到它的上下文中:

<bean id="appContext" class="my.package.MyContextWrapper">
</bean>

现在,这个bean可以通过引用加载到应用程序的任何其他bean中:

<bean id="myBeanFactory" class="my.package.MyBeanFactory">
 <property name="springContext" ref="appContext">
 </property>
</bean>

使用GenericBean定义加载bean定义:

BeanDefinitionRegistry registry = ((BeanDefinitionRegistry )factory);

GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyBeanClass.class);
beanDefinition.setLazyInit(false);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setScope("session");

registry.registerBeanDefinition("dynamicBean",beanDefinition);

Bean在会话范围内创建,并将存储在用户会话中。属性auto-wire-candidate告诉spring bean的依赖项(如setter、getter或构造函数参数)是否应该由spring自动处理。属性lazy init告诉Spring是否应该在需要时实例化这个bean。

要获得Spring bean的句柄,请使用Spring应用程序上下文,如下所示:

Object bean= 
 getApplicationContext().getBean("dynamicBean");
 if(bean instanceof MyBeanClass){
 MyBeanClass myBean = (MyBeanClass) bean;

   // do with the bean what ever you have to do.
 } 
孙修德
2023-03-14

这纯粹是我的观点,不是专家的观点:

Spring为应用程序上下文的定制修改提供了两种机制——使用BeanFactoryPostProcessor(允许修改现有bean定义或添加新的bean定义)和BeanPostProcessor(允许修改bean实例(围绕代理等进行包装)。

Spring不提供任何其他本机方式来在运行时动态添加bean定义或bean实例,但就像您所做的那样,获取底层bean工厂实例并添加bean定义是一种方法。这是可行的,但也有风险:

>

这个新注册的bean不会自动连接任何字段,也不会被注入到其他bean中——因此本质上,bean工厂只是作为保存bean的注册表,而不是真正的依赖注入功能!

如果在应用程序上下文中调用了refresh(),那么支持bean工厂将被覆盖,并创建一个新的工厂,因此直接针对bean工厂注册的任何bean实例都将丢失。

如果目标纯粹是创建Spring自动连接的bean,我会选择@Configurable之类的东西。如果上述风险可以接受,你的方法也应该有效。

 类似资料:
  • 问题内容: 只需要一些由社区评估的东西。以下是一段代码,这是一个创建特定类型实例的简单工厂。该方法将在上下文中将bean注册为原型并返回实例。这是我第一次在运行时配置bean。您能否评价并提供反馈?先感谢您。 FLEX Sync工厂已在IoC容器中配置为单例。因此,要创建新的同步管理器,请执行以下操作: 我正在使用Spring 3.1。请查看并提供您宝贵的反馈意见。 亲切的问候。 问题答案: 这纯

  • 我正在搜索一个在运行时注册@RestController的解决方案。我目前正在向

  • 我有一个具有两个endpoint的服务: 公共endpoint:任何人都可以访问它,并打开用户帐户(注册) 受保护endpoint:只有注册用户才能访问它,将授权头用作HTTP POST请求的一部分 用例: 用户首先到达公共endpoint并通过HTTP POST打开帐户,该JSON包含。然后服务生成密码,并将其作为JSON响应传递回用户。 用户从服务中拿回密码后,他应该使用这个(连同他的)通过在

  • 我知道ApplicationContext扩展点,如ApplicationContext事件和BeanFactoryPostProcessor。 我手头的问题是,在创建了一些bean之后,我需要添加bean,我想这会丢弃BeanFactoryPostProcessor选项,因为在应用程序上下文开始注册bean之前会发生这种情况。 我尝试在上下文刷新后添加一个singletonBean:

  • 问题内容: 我试图将服务注入到正在使用Jersey的Rest类中。 无论我尝试如何或以何种方式插入此类,似乎在运行时都显示为null。查看日志文件表明,在初始化Web应用程序时正在调用setJsonTestService,并且此时它不为null。但是,当稍后通过对此类的PUT请求访问它时,它为null。 我完全困惑。 该类如下所示: 有任何想法吗? 更新: web.xml(泽西) 问题答案: 尝试

  • 问题内容: 我正在使用带有注释的Spring Bean,并且需要在运行时选择其他实现。 例如,我需要Windows平台,我需要Linux平台。 目前,我只知道一种可怕的解决方案: 请考虑我仅使用注释,而不使用XML配置。 问题答案: 您可以将bean注入移动到配置中,如下所示: 或者,您可以使用配置文件和,然后用注释(如或)注释您的服务实现,并为您的应用程序提供此配置文件之一。