Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I ensure dependent configurations are initialized with Spring @Configuration annotation?

Tags:

java

spring

I am trying to use @Configuration annotations to wire up my application but I keep getting a NullPointerException in one of the initializers because the bean it refers to is not yet initialized (I think). I have tried specifying in the web.xml just the 'root' config class and also tried doing a package scan and neither seem to work.

Sorry about the big code dump. I tried to produce a much simpler set of classes to reproduce the issue, but of course, when I did that, everything worked fine. Here are my classes (imports elided):

DataSourceConfig.java:

@Configuration
public class DataSourceConfig {

    public DataSourceConfig() {
        System.err.println("DataSourceConfig constructed...");
    }

    @Bean
    public DataSource dataSource() {
        BasicDataSource bean = new BasicDataSource();
        bean.setDriverClassName("com.mysql.jdbc.Driver");
        bean.setUrl("jdbc:mysql://localhost:3306/observation");
        bean.setUsername("observation");
        bean.setPassword("*******");
        bean.setInitialSize(1);
        bean.setMaxActive(5);
        bean.setTestOnBorrow(true);
        System.err.println("dataSource bean initialized: " + bean.toString());
        return bean;
    }
}

HibernateConfig.java

@Configuration
@Import(DataSourceConfig.class)
public class HibernateConfig {

    public HibernateConfig() {
        System.err.println("HibernateConfig constructing...");
    }

    @Autowired
    private DataSourceConfig dataSourceConfig;

    @Bean
    protected NamingStrategy namingStrategy() {
        return new ImprovedNamingStrategy();
    }

    private AnnotationSessionFactoryBean sessionFactoryBean = null;

    @Bean
    @DependsOn("dataSourceConfig")
    public AnnotationSessionFactoryBean sessionFactory() {
        if (sessionFactoryBean == null) {
            sessionFactoryBean = new AnnotationSessionFactoryBean();
NPE Here--> sessionFactoryBean.setDataSource(dataSourceConfig.dataSource()); 
            sessionFactoryBean.setSchemaUpdate(true);
            sessionFactoryBean.setNamingStrategy(namingStrategy());
            sessionFactoryBean.setPackagesToScan(new String[] {
                    "com.newco.observations.domain",
                    "com.newco.observations.domain.*" });
            Properties props = new Properties();
            props.setProperty("hibernate.default_schema", "observation");
            props.setProperty("hibernate.dialect",
                    "org.hibernate.dialect.MySQLDialect");
            props.setProperty("hibernate.show_sql", "true");
            sessionFactoryBean.setHibernateProperties(props);
            System.err.println("sessionFactory initialized");
        }
        return sessionFactoryBean;
    }

    @Bean
    @DependsOn("dataSourceConfig")
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSourceConfig.dataSource());
    }

    @Bean
    @DependsOn("sessionFactory")
    public ResourceTransactionManager txManager() {
        HibernateTransactionManager bean = new HibernateTransactionManager();
        bean.setSessionFactory((SessionFactory) sessionFactory().getObject());
        return bean;
    }

    @Bean
    @DependsOn("sessionFactory")
    public HibernateTemplate hibernateTemplate() {
        return new HibernateTemplate((SessionFactory) sessionFactory()
                .getObject());
    }
}

DaoConfig.java:

@Configuration
@Import(HibernateConfig.class)
public class DaoConfig {

    public DaoConfig()
    {
        System.err.println("DaoConfig constructing...");
    }

    private @Autowired HibernateConfig hibernateConfig;

    @Bean
    @DependsOn("hibernateTemplate")
    public PhenomenonGroupDao phenomenonGroupDao()
    {
        PhenomenonGroupDaoImpl bean = new PhenomenonGroupDaoImpl();
        bean.setHibernateTemplate(hibernateConfig.hibernateTemplate());
        return bean;
    }

    @Bean
    @DependsOn("hibernateTemplate")
    public PhenomenonDao phenomenonDao()
    {
        PhenomenonDaoImpl bean = new PhenomenonDaoImpl();
        bean.setHibernateTemplate(hibernateConfig.hibernateTemplate());
        return bean;
    }

    @Bean
    @DependsOn("hibernateTemplate")
    public DiscretePhenomenonDao discretePhenomenonDao()
    {
        DiscretePhenomenonDaoImpl bean = new DiscretePhenomenonDaoImpl();
        bean.setHibernateTemplate(hibernateConfig.hibernateTemplate());
        return bean;
    }


}

You can see from the System.err.println's and the @DependsOn annotations a kind of flailing about that I'm doing.

I can provide the full log if it's useful, but here is what I think are the relevant lines (with a little formatting to make it more readable (maybe)):

  • 208 [Thread-0] INFO org.springframework.context.annotation.ConfigurationClassEnhancer
  • Successfully enhanced com.bjk.observation.server.config.DaoConfig; enhanced class name is: com.bjk.observation.server.config.DaoConfig$$EnhancerByCGLIB$$96e1956
  • 229 [Thread-0] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory
  • Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@185572a: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalRequiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.annotation.internalPersistenceAnnotationProcessor, daoConfig,com.bjk.observation.server.config.DataSourceConfig#0, dataSource, com.bjk.observation.server.config.HibernateConfig#0, namingStrategy, sessionFactory, jdbcTemplate, txManager, hibernateTemplate, phenomenonGroupDao, phenomenonDao, discretePhenomenonDao]; root of factory hierarchy DaoConfig constructing...
  • 252 [Thread-0] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory
  • Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@185572a: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalRequiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.annotation.internalPersistenceAnnotationProcessor, daoConfig, com.bjk.observation.server.config.DataSourceConfig#0, dataSource, com.bjk.observation.server.config.HibernateConfig#0, namingStrategy, sessionFactory, jdbcTemplate, txManager, hibernateTemplate, phenomenonGroupDao, phenomenonDao, discretePhenomenonDao]; root of factory hierarchy
  • 253 [Thread-0] ERROR org.springframework.web.context.ContextLoader
  • Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'daoConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bjk.observation.server.config.HibernateConfig com.bjk.observation.server.config.DaoConfig.hibernateConfig; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.bjk.observation.server.config.HibernateConfig#0': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.bjk.observation.server.config.HibernateConfig]: Constructor threw exception; nested exception is java.lang.NullPointerException
like image 304
jhericks Avatar asked Jul 16 '10 00:07

jhericks


1 Answers

The problem, I believe is here:

@Autowired
private DataSourceConfig dataSourceConfig;

You're not supposed to explicitly wire yourself with other @Configuration-annotated classes, but rather the beans that they produce. Spring will sort out the plumbing for you.

So replace the above field with the simpler:

@Autowired
private DataSource dataSource;

Spring will fetch the DataSource from DataSourceConfig and transparently inject it into the field.

Similarly, replace

@Autowired 
private HibernateConfig hibernateConfig;

with

@Autowired 
private HibernateTemplate hibernateTemplate;

You'll notice that the @Configuration style doesn't feel as nice when working with factory beans like AnnotationSessionFactoryBean, since you often have to call getObject() on it yourself. Sometimes, it's more natural to use XML config, and mix it with the java config style.

like image 165
skaffman Avatar answered Sep 19 '22 15:09

skaffman