Подтвердить что ты не робот

Регистрация beans (прототип) во время выполнения в Spring

Просто нужно что-то оценивать сообществом. Ниже приведен фрагмент кода, который представляет собой простой factory, который создает экземпляры определенного типа. Метод зарегистрирует bean в контексте в качестве прототипа и вернет экземпляр. Это первый раз, когда я настраиваю beans во время выполнения. Могли бы вы любезно оценить и предоставить обратную связь? заранее спасибо.

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 factory был сконфигурирован в контейнере IoC как однострочный. Поэтому для создания нового диспетчера синхронизации я делаю следующее:

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

Я использую Spring 3.1. Пожалуйста, просмотрите и предоставите ценную информацию.

С уважением.

4b9b3361

Ответ 1

Это чисто мое мнение, а не экспертное мнение:

Spring предоставляет два механизма пользовательской модификации контекста приложения - с помощью BeanFactoryPostProcessor, который позволяет модифицировать существующие определения bean или добавления новых определений bean и BeanPostProcessors, который позволяет модифицировать экземпляры bean (обертывать их вокруг прокси и т.д.).

Spring не предоставляет другого родного способа динамически добавлять определения bean или bean экземпляры во время выполнения, но, как вы это сделали, получив базовые bean factory экземпляры и добавив в bean Определения - один из способов. Он работает, но есть риски:

  • Что произойдет, если вы перезапишете существующее имя bean новым типом, как места, где этот bean уже обрабатывается. Кроме того, что произойдет, если существующее имя bean будет перезаписано совершенно другим типом!

  • Этот недавно зарегистрированный bean не будет иметь никаких полей, которые были бы автоматически добавлены и не будут также введены в другой beans, поэтому по существу bean factory просто выступает в качестве реестра для хранения bean, на самом деле не функциональность инъекций зависимостей!

  • если в контексте приложения вызывается refresh(), тогда резервная копия bean factory будет перезаписана и будет создана новая, поэтому любые экземпляры bean, зарегистрированные в bean factory напрямую будет потерян.

Если цель заключается в создании beans, который был автопрограммирован Spring, я бы выбрал что-то вроде @Configurable. Если вышеприведенные риски приемлемы, ваш подход должен работать.

Ответ 2

Это сработало для меня: 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

}

Пусть Spring загрузит этот bean в его контекст, объявив bean в файле конфигурации XML:

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

Теперь этот bean может быть загружен в любой другой bean приложения посредством ссылки на него:

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

Используйте GenericBeanDefinition для загрузки определения 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 создается в области сеанса и будет храниться в сеансе пользователя. Кандидат с автопроводом свойств сообщает Spring, если зависимость bean, такая как параметр setter или getter или constructor, должна обрабатываться автоматически с помощью 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.
 } 

Ответ 3

Ваше решение выглядит хорошо. Я считаю, что мы также можем достичь, создав 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;
    }

}