How to connect to a database inside a BeanFactoryPostProcessor?

516 Views Asked by At

I am working on a project using Spring Data JPA (on Tomcat 7). I'am implementing a BeanFactoryPostProcessor to dynamically create my DataSources. But the problem is that my DataSource's information (name, url, etc..) is stored in a database itself.

@Component 
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 

  // This doesn't work
  @Autowired DatabaseService databaseService;

  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // my code here ...
    // ...
  }
}

As you can see, i was trying to inject a service which can get me a list of all DataSources from my database, but it doesn't work. Is the anyway to connect to the database and get that list within the BeanFactoryPostProcessor class? Any other workaround will be welcome. :)

1

There are 1 best solutions below

0
On

BeanFactoryPostProcessors are a very special kind of concept in Spring. They are components that operate on BeanDefinition instances, which are a metamodel of the bean instances to be created.

That means, that at the point in time when the BFPPs are invoked, no bean instances have been created yet as the metamodel is about to be post processed (as the name suggests). Hence beans depended on by the BFPP will be initialized extremely early in the lifecycle of the container. Thus it's highly recommended to not depend on application components from BFPPs or - if really required - only on beans that don't necessarily trigger the creation of a lot of downstream components.

That said, you shouldn't depend on especially repositories from BFPPs as they usually require the creation of a lot of infrastructure components. I'd recommend getting the configuration properties that are required to connect to the configuration database (JDBC URL, username, password, etc.) and just create a throw-away DataSource that's only used to create a new BeanDefinition for a new DataSource that's going to be used by the application eventually.

So here are the recommended steps (from the top of my head - might need some tweaking):

  • drop the autowiring of a DataSource
  • configure an @PropertySource pointing to the properties containing the coordinates to connect
  • inject the values of that PropertySource into the constructor of the BFPP:

    class YourBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
      public YourBeanFactoryPostProcessor(@Value("#{properties.url}) String url, …) {
        // assign to fields
      }
    
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    
        // 1. Create throw-away DataSource
        // 2. Create JdbcTemplate
        // 3. Use template to lookup configuration properties
        // 4. Create BeanDefinition for DataSource using properties just read
        // 5. Register BeanDefinition with the BeanFactory
      }
    }