Search code examples
javaspringinversion-of-controlspring-annotations

FactoryBeans and the annotation-based configuration in Spring 3.0


Spring provides the FactoryBean interface to allow non-trivial initialisation of beans. The framework provides many implementations of factory beans and -- when using Spring's XML config -- factory beans are easy to use.

However, in Spring 3.0, I can't find a satisfactory way of using factory beans with the annotation-based configuration (née JavaConfig).

Obviously, I could manually instantiate the factory bean and set any required properties myself, like so:

@Configuration
public class AppConfig {

...

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource());
        factory.setAnotherProperty(anotherProperty());

        return factory.getObject();
    }

However, this would fail if the FactoryBean implemented any Spring-specific callback interfaces, like InitializingBean, ApplicationContextAware, BeanClassLoaderAware, or @PostConstruct for example. I also need to inspect the FactoryBean, find out what callback interfaces it implements, then implement this functionality myself by calling setApplicationContext, afterPropertiesSet() etc.

This feels awkward and back-to-front to me: application-developers should not have to implement the callbacks of the IOC container.

Does anyone know of a better solution to using FactoryBeans from Spring Annotation configs?


Solution

  • As far as I understand your problem is what you want a result of sqlSessionFactory() to be a SqlSessionFactory (for use in other methods), but you have to return SqlSessionFactoryBean from a @Bean-annotated method in order to trigger Spring callbacks.

    It can be solved with the following workaround:

    @Configuration 
    public class AppConfig { 
        @Bean(name = "sqlSessionFactory")
        public SqlSessionFactoryBean sqlSessionFactoryBean() { ... }
    
        // FactoryBean is hidden behind this method
        public SqlSessionFactory sqlSessionFactory() {
            try {
                return sqlSessionFactoryBean().getObject();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    
        @Bean
        public AnotherBean anotherBean() {
            return new AnotherBean(sqlSessionFactory());
        }
    }
    

    The point is that calls to @Bean-annotated methods are intercepted by an aspect which performs initialization of the beans being returned (FactoryBean in your case), so that call to sqlSessionFactoryBean() in sqlSessionFactory() returns a fully initialized FactoryBean.