将预构造的Bean添加到Spring应用程序上下文中

时间:2009-01-30 18:52:31

标签: java spring

我正在编写一个实现以下方法的类:

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 个答案:

答案 0 :(得分:31)

我的情况完全相同。由于没有人提出我的解决方案(我认为我的解决方案更优雅),我将在这里为后代添加它: - )

解决方案包括两个步骤:

  1. 创建父ApplicationContext并在其中注册现有的bean。
  2. 创建子ApplicationContext(在父上下文中传递)并从XML文件
  3. 加载bean

    步骤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);
    

答案 1 :(得分:15)

我发现可以使用两个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定义现在简化为:

<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");
}

事情似乎成功了!

答案 2 :(得分:3)

由于刷新问题,第二种解决方案会导致异常。更优雅的方法是将对象添加到上下文中,然后使用xmlreader加载xml定义。 因此:

 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>

完美无缺!

答案 3 :(得分:1)

您可以为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并将其连接到所有bean中。然后在您的方法中,您将获得对DataSourceWrapper的引用,并将包装的DataSource设置为传递给您的方法的DataSource。

这一切都高度依赖于Spring上下文文件加载时发生的事情。如果bean在上下文加载时要求DataSource已经可用,那么你可能必须编写一个BeanFactoryPostProcessor来加载Spring上下文文件,而不是在加载后做一些事情(尽管可能是lazy-init)可以解决这个问题。)

答案 4 :(得分:0)

如果通过调用“new”创建对象,则它不受Spring工厂的控制。

为什么不让Spring将DataSource注入对象而不是将其传递给run()?

答案 5 :(得分:0)

有一种更优雅的方式,您可以使用外部xml文件并使用文件系统资源加载它,然后将配置在其中的bean注入您的应用程序上下文。因此:

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加载类并将bean加载到应用程序上下文中。

希望这有助于某人。