Добавление предварительно созданного компонента в контекст приложения Spring - PullRequest
23 голосов
/ 30 января 2009

Я пишу класс, который реализует следующий метод:

public void run(javax.sql.DataSource dataSource);

В этом методе я хочу построить контекст приложения Spring, используя файл конфигурации, подобный следующему:

<bean id="dataSource" abstract="true" />

<bean id="dao" class="my.Dao">
  <property name="dataSource" ref="dataSource" />
</bean>

Можно ли заставить Spring использовать объект DataSource, переданный моему методу, везде, где указан объект bean "dataSource" в файле конфигурации?

Ответы [ 6 ]

31 голосов
/ 08 марта 2010

Я был в точно такой же ситуации. Поскольку никто не предложил мое решение (и я думаю, что мое решение более изящно), я добавлю его сюда для будущих поколений:

Решение состоит из двух этапов:

  1. создайте родительский ApplicationContext и зарегистрируйте в нем существующий компонент.
  2. создать дочерний ApplicationContext (передать в родительском контексте) и загрузить бины из XML-файла

Шаг № 1:

//create parent BeanFactory
DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();
//register your pre-fabricated object in it
parentBeanFactory.registerSingleton("dataSource", dataSource);
//wrap BeanFactory inside ApplicationContext
GenericApplicationContext parentContext = 
        new GenericApplicationContext(parentBeanFactory);
parentContext.refresh(); //as suggested "itzgeoff", to overcome a warning about events

Шаг № 2:

//create your "child" ApplicationContext that contains the beans from "beans.xml"
//note that we are passing previously made parent ApplicationContext as parent
ApplicationContext context = new ClassPathXmlApplicationContext(
        new String[] {"beans.xml"}, parentContext);
15 голосов
/ 31 января 2009

Я обнаружил, что два Spring-интерфейса могут быть использованы для реализации того, что мне нужно. Интерфейс BeanNameAware позволяет Spring сообщать объекту его имя в контексте приложения, вызывая метод setBeanName (String) . Интерфейс FactoryBean указывает Spring не использовать сам объект, а объект, возвращаемый при вызове метода getObject () . Соедините их вместе, и вы получите:

public class PlaceholderBean implements BeanNameAware, FactoryBean {

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

    private String beanName;

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

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

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

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

}

Определение бина теперь уменьшено до:

<bean id="dataSource" class="PlaceholderBean" />

Заполнитель получает свое значение до создания контекста приложения.

public void run(DataSource externalDataSource) {
    PlaceholderBean.beansByName.put("dataSource", externalDataSource);
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    assert externalDataSource == context.getBean("dataSource");
}

Кажется, все работает успешно!

3 голосов
/ 21 сентября 2010

Второе решение вызывает исключение из-за проблемы обновления. Более элегантным способом будет добавление объектов в контекст, а затем загрузка определений XML с использованием xmlreader. Таким образом:

 ObjectToBeAddedDynamically objectInst = new ObjectToBeAddedDynamically();
  DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();  
  parentBeanFactory.registerSingleton("parameterObject", objectInst);

  GenericApplicationContext parentContext = new GenericApplicationContext(parentBeanFactory);

  XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(parentContext);
   xmlReader.loadBeanDefinitions(new FileSystemResource("beandefinitions.xml"));
   parentContext.refresh();

   ObjectUsingDynamicallyAddedObject userObjectInst= (ObjectUsingDynamicallyAddedObject )parentContext.getBean("userObject");

и

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userObject" class="com.beanwiring.ObjectUsingDynamicallyAddedObject"
      >
      <constructor-arg ref="parameterObject" />

</bean>

</beans>

отлично работает!

1 голос
/ 30 января 2009

Вы можете создать класс-оболочку для DataSource, который просто делегирует содержащемуся DataSource

public class DataSourceWrapper implements DataSource {

DataSource dataSource;

public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
}

@Override
public Connection getConnection() throws SQLException {
    return dataSource.getConnection();
}

@Override
public Connection getConnection(String username, String password)
        throws SQLException {
    return dataSource.getConnection(username, password);
}
//delegate to all the other DataSource methods
}

Затем в вашем файле контекста Spring вы объявляете DataSourceWrapper и подключаете его ко всем вашим компонентам. Затем в вашем методе вы получаете ссылку на DataSourceWrapper и устанавливаете обернутый DataSource на тот, который был передан вашему методу.

Эта работа в значительной степени зависит от того, что происходит в вашем контекстном файле Spring при его загрузке. Если компонент требует, чтобы DataSource был уже доступен при загрузке контекста, вам, возможно, придется написать BeanFactoryPostProcessor, который изменяет контекстный файл Spring при загрузке, а не делать что-то после загрузки (хотя, возможно, ленивый init может решить эту проблему). этот выпуск).

0 голосов
/ 06 августа 2016

Существует более элегантный способ, с помощью которого вы можете использовать внешний XML-файл и загружать его с помощью ресурса файловой системы, а затем вставлять настроенные в нем компоненты в контекст приложения. Таким образом:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;

@Service
@Order(-100)
public class XmlBeanInitializationService implements ApplicationContextAware, InitializingBean {

    private ApplicationContext applicationContext;

    @Value("${xmlConfigFileLocation}")
    private String xmlConfigFileLocation;

    @Override
    public void afterPropertiesSet() throws Exception {
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry)applicationContext);
        reader.loadBeanDefinitions(new FileSystemResource(xmlConfigFileLocation));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;

    }
}

где $ {xmlConfigFileLocation} - это свойство, указанное в файле application.properties, которое указывает на местоположение файла в вашей системе, таким образом:

xmlConfigFileLocation="your-file-path-anywhere-in-your-system"

и ваш xml-файл может содержать:

<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean class="com.yourpackage.YourBean1Class"></bean>
        <bean class="com.yourpackage.YourBean2Class"></bean>
        <bean class="com.yourpackage.YourBean3Class"></bean>

    </beans>

Таким образом, когда ваше приложение запускает Spring, загружает класс и загружает компонент в контекст приложения.

Надеюсь, это кому-нибудь поможет.

0 голосов
/ 30 января 2009

Если вы создаете объект, вызывая «new», он не находится под контролем фабрики Spring.

Почему бы Spring не вставить DataSource в объект вместо того, чтобы передавать его в run ()?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...