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

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

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

在此方法中,我希望使用类似于以下内容的配置文件构造Spring应用程序上下文:

     

是否有可能强制Spring使用传递给我的方法的DataSource对象,只要在配置文件中引用“dataSource”bean?

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

该解决方案包括两个步骤:

  1. 创建父ApplicationContext并在其中注册现有的bean。
  2. 创建子ApplicationContext(在父上下文中传递)并从XML文件加载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); 

我发现可以使用两个Spring接口来实现我需要的东西。 BeanNameAware接口允许Spring通过调用setBeanName(String)方法在应用程序上下文中告诉对象其名称。 FactoryBean接口告诉Spring不要使用对象本身,而是调用getObject()方法时返回的对象。 把它们放在一起你得到:

 public class PlaceholderBean implements BeanNameAware, FactoryBean { public static Map beansByName = new HashMap(); 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定义现在简化为:

  

占位符在创建应用程序上下文之前接收其值。

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

事情似乎成功了!

由于刷新问题,第二种解决方案会导致exception。 更优雅的方法是将对象添加到上下文中,然后使用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"); 

        

工作完美!

您可以为简单地委托给包含的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可以解决这个问题) )。

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

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

有一种更优雅的方式,您可以使用外部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文件可能包含:

       

因此当你的应用程序启动时,spring加载类并将bean加载到应用程序上下文中。

希望这有助于某人。