Adding beans breaks Spring Configuration - java

EDIT 1:
I'm currently calling this from a Main class like so:
public class Main
{
public static void main(String[] args)
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringAppConfig.class);
DataSource dSource = ctx.getBean(DataSource.class);
System.out.println(dSource.getClass().toString());
if (dSource instanceof Log4jdbcProxyDataSource)
{
Log4jdbcProxyDataSource log4jdbcProxyDataSource = (Log4jdbcProxyDataSource) dSource;
Object lf = log4jdbcProxyDataSource.getLogFormatter();
System.out.println(lf.getClass().toString());
}
System.exit(0);
}
}
Original:
Code follows after explanation:
I have a Spring application with a JavaConfig, call it the primary app, that imports another Spring JavaConfig class from a library. This imported JavaConfig is supposed to wrap any DataSource created in the primary app with an Aspect, which has an autowired LogDelegator.
As long as the primary app contains only a DataSource, everything works. But as soon as I add an EntityManager to the primary app, I get a nested IllegalArgumentException saying that the LogDelegator is null.
Primary App's Config:
#Configuration
#Import(MonitoringConfig.class)
public class SpringAppConfig
{
#Bean
public DataSource dataSource()
{
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2).build();
return db;
}
}
Imported Library Config:
#Configuration
#EnableSpringConfigured
public class MonitoringConfig extends WebMvcConfigurerAdapter
{
#Bean
public LogDelegator logDelegator()
{
return new LogDelegator();
}
#Bean
public ConfigurationAspect configurationAspect()
{
return Aspects.aspectOf(ConfigurationAspect.class);
}
}
The Aspect:
#Configurable
public aspect ConfigurationAspect
{
#Autowired
LogDelegator logDelegator;
Object around() : execution(public DataSource (#Configuration *).*(..)) {
Object ret = proceed();
if (ret instanceof DataSource) {
Log4jdbcProxyDataSource dataSource = new Log4jdbcProxyDataSource((DataSource) ret);
dataSource.setLogFormatter(logDelegator);
return dataSource;
} else {
return ret;
}
}
This code works great until I add the following,
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setDatabase(Database.H2);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
return factory;
}
#Bean
public EntityManager entityManager()
{
return entityManagerFactory().getObject().createEntityManager();
}
#Bean
public PlatformTransactionManager transactionManager()
{
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return jpaTransactionManager;
}
and then I get:
java.lang.reflect.InvocationTargetException at java.lang.Thread.run(Thread.java:722)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class SpringAppConfig: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [publ
ic org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean com.fl.sas.configurable.config.SpringAppConfig.entityManagerFactory()] threw exception; ne
sted exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class SpringAppConfig: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public
javax.sql.DataSource SpringAppConfig.dataSource()] threw exception; nested exception is java.lang.IllegalArgumentException: log4j
dbc: logDelegator cannot be null.
Can anyone help?

I needed to add
#Depends(value="configurationAspect") on dataSource()
Luca Basso Ricci answered the question. If he ever adds the answer, I'll give him the credit. :)

Related

Spring boot / Spring security with dedicated database for security

I am looking desesperatly for spring boot running with spring security configured with two different databases. One for application's data and the other one for spring security only.
I have worked with https://www.baeldung.com/spring-data-jpa-multiple-databases for multiple databases configurations.With that too https://www.laulem.com/dev/spring-boot/spring-security.html for spring security.
But after several work, I'am unable to have a working project with spring boot and spring security with two separates database.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = "com.myApp.security.repositories",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").successHandler( myAuthenticationSuccessHandler() ).failureUrl("/login?error=true").permitAll()
.and().logout().deleteCookies("JSESSIONID").logoutUrl("/logout").logoutSuccessUrl("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(myAppPasswordEncoder());
}
/**
* Cryptage des mots de passe
*
* #return
*/
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* No password encryption (dev only!!!)
*
* #return
*/
#Bean
public myAppPasswordEncoder myAppPasswordEncoder() {
return new myAppPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new myAppUrlAuthenticationSuccessHandler();
}
#Bean
#ConfigurationProperties(prefix="spring.productdb-datasource")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan("com.myApp.security.metier.impl");
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
final HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager productTransactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(productEntityManager().getObject());
return transactionManager;
}
}
I get this kind of errors
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'securityConfig':
Unsatisfied dependency expressed through field 'customUserDetailsService';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userDetailsService':
Unsatisfied dependency expressed through field 'userService';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userService':
Unsatisfied dependency expressed through field 'userRepository';
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'userRepository' defined in com.myApp.security.repositories.UserRepository defined in #EnableJpaRepositories declared on SecurityConfig:
Cannot create inner bean '(inner bean)#2239ae10' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager';
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#2239ae10':
Cannot resolve reference to bean 'productEntityManager' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'productEntityManager': Requested bean is currently in creation:
Is there an unresolvable circular reference?
Edit:
The problem seems to be caused by WebSecurityConfigurerAdapter inheritance. Some beans are instanciated by this class and create conflict with the dedicated datasource I try to configure for the "security" part of my spring-boot app.
I'm still searching...
Edit 2:
If anyboby's got a git URL to a working sample example...

Unresolveable Circular reference error when configuring multiple datasources in a spring batch

I'm trying to configure two datasources in my spring batch application. One for batch metadata tables, and another for the business tables.
Snippet from my application.properties file:
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=
spring.batchdatasource.url=
spring.batchdatasource.username=
spring.batchdatasource.password=
spring.batchdatasource.driver-class-name=
My batch config file:
#Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
// #Autowired
// private DataSource dataSource;
#Autowired
private PlatformTransactionManager transactionManager;
#Bean(name = "batchDatasource")
#ConfigurationProperties(prefix="spring.batchdatasource")
public DataSource batchDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryDatasource")
#ConfigurationProperties(prefix="spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Override
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(batchDataSource());
// factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("schema1"+ ".BATCH_");
factory.afterPropertiesSet();
return factory.getObject();
}
/* Job and step bean definitions here */
My main class is the one annotated with #EnableBatchProcessing
#SpringBootApplication
#EnableBatchProcessing
public class SpringBatchExample1Application {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
I'm getting this Requested bean is currently in creation: Is there an unresolvable circular reference? when trying to configure two datasources. It works fine when using a single datasource by autowiring(refer the commented out lines of code) instead of creating multiple beans.
Following is the exception snippet:
Error creating bean with name 'springBatchConfig': Unsatisfied dependency expressed through method 'setDataSource' parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'batchDatasource': Requested bean is currently in creation: Is there an unresolvable circular reference?
I looked up and found out this occurs when there's a dependency on a bean which is still not created or is being created. I just see it in the createJobRepository method where datasource is being plugged. I still face the error even if I don't have the createJobRepository method.
It seems like the requirement is for the datasource beans to be created before others. I tried using the #Order annotation, but no luck.
EDIT:
I tried the solution from #Mykhailo Skliar's Accepted answer below, and serparated the Datasource beans into a new Configuration class. Though it resolved the initial Unresolveble circular reference issue anymore, it led me to the following error:
Error creating bean with name 'springBatchConfig': Invocation of init method failed; nested exception is org.springframework.batch.core.configuration.BatchConfigurationException: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
Based on this answer, I changed my url property names as follows:
spring.datasource.jdbc-url=
spring.datasource.jdbc-url=
Though it solved the jdbcUrl error, it posed another issue:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Reference to database and/or server name in 'sample-sql-server.schema1.MY_TABLE_NAME' is not supported in this version of SQL Server.
Both my data sources are Azure SQL server instances.
I looked up and found it was not possible to use multiple Azure SQL databases years ago, but based on this answer it should not be the case anymore.
The issue is most probably because of
factory.setDataSource(batchDataSource());
You should use autowired bean here, instead of calling batchDataSource()
I would split SpringBatchConfig in two beans:
#Configuration
public class DataSourceConfig {
#Bean(name = "batchDatasource")
#ConfigurationProperties(prefix="spring.batchdatasource")
public DataSource batchDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryDatasource")
#ConfigurationProperties(prefix="spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Qualifier("batchDataSource")
#Autowired
private DataSource batchDataSource;
#Autowired
private PlatformTransactionManager transactionManager;
#Override
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(batchDataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("schema1"+ ".BATCH_");
factory.afterPropertiesSet();
return factory.getObject();
}
}

Circular reference involving containing bean 'XXX' - consider declaring the factory method as static for independence from its containing instance

I am creating two projects globalDB and GlobalWeb using spring. I want to create multiple database connections in GlobalWeb, Jdbc properties are fetched from GlobalDB project dao layer at the time of GlobalWeb initiation. When i run the global web, Am getting the following exception.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalWebConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.kla.it.dao.Dao com.kla.it.global.conf.GlobalWebConfig.dao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'daoImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.jdbc.core.JdbcTemplate com.kla.it.dao.impl.DaoImpl.template; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'template' defined in class path resource [com/kla/it/database/DatabaseConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.sql.DataSource]: : Error creating bean with name 'partsDs' defined in com.kla.it.global.conf.GlobalWebConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.jdbc.datasource.DriverManagerDataSource]: Circular reference involving containing bean 'globalWebConfig' - consider declaring the factory method as static for independence from its containing instance. Factory method 'getPartsDataSource' threw exception; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'partsDs' defined in com.kla.it.global.conf.GlobalWebConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.jdbc.datasource.DriverManagerDataSource]: Circular reference involving containing bean 'globalWebConfig' - consider declaring the factory method as static for independence from its containing instance. Factory method 'getPartsDataSource' threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4680)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5143)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:705)
GlobalDB configuration class
#Configuration
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:database.properties"})
public class DatabaseConfig {
#Autowired
private Environment env;
// #Bean(name="ds")
public DataSource getDataSource(){
System.out.println("++++++++++++++++DATABASE++++++++++++++START++++++++++++");
System.out.println("Method to crate data source");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("net.snowflake.client.jdbc.SnowflakeDriver");
ds.setUrl(env.getRequiredProperty("db.url"));
ds.setUsername(env.getRequiredProperty( "db.username"));
ds.setPassword(env.getRequiredProperty( "db.password"));
return ds;
}
#Bean(name="template")
public JdbcTemplate jdbcTemplate(DataSource ds) {
System.out.println("Jdbc template method called.");
JdbcTemplate template = new JdbcTemplate(ds);
template.setResultsMapCaseInsensitive(true);
System.out.println("++++++++++++++++DATABASE++++++++++++++END+++++++++++");
return template;
}
}
GlobalDB Dao Layer class
#Repository
#Qualifier("dao")
public class DaoImpl implements Dao{
#Autowired
JdbcTemplate template;
#Override
public Map<String, String> getConnectionsData(String appName, String env) {
List<Map<String, Object>> reportsList = new ArrayList<>();
Map<String, String> dataSourceMap = new HashMap<>();
String qry = "SELECT * FROM DB_CONFIG WHERE name = '"+appName+"' AND ev = '"+env+"'";
reportsList = template.queryForList(qry);
int count = 1;
for (Map<String, Object> map : reportsList) {
dataSourceMap.put(String.valueOf(map.get("prop")), String.valueOf(map.get("val")));
}
return dataSourceMap;
}
}
GlobalWeb Config class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:global.properties"})
public class GlobalWebConfig extends WebMvcConfigurerAdapter {
#Autowired(required=true)
Dao dao;
#Bean(name = "partsDs")
public DriverManagerDataSource getPartsDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("parts", "dev");
DriverManagerDataSource partsDs= new DriverManagerDataSource();
partsDs.setDriverClassName(dsMap.get("driver"));
partsDs.setUrl(dsMap.get("url"));
partsDs.setUsername(dsMap.get("user"));
partsDs.setPassword(dsMap.get("password"));
return partsDs;
}
#Bean(name = "cosmosDs")
public DriverManagerDataSource getCosmosDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("cosmos", "dev");
DriverManagerDataSource cosmosDs = new DriverManagerDataSource();
cosmosDs.setDriverClassName(dsMap.get("driver"));
cosmosDs.setUrl(dsMap.get("url"));
cosmosDs.setUsername(dsMap.get("user"));
cosmosDs.setPassword(dsMap.get("password"));
return cosmosDs;
}
#Bean(name = "itsDs")
public DataSource getITSDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("ITSecurity", "dev");
DriverManagerDataSource itsDs= new DriverManagerDataSource();
itsDs.setDriverClassName(dsMap.get("driver"));
itsDs.setUrl(dsMap.get("url"));
itsDs.setUsername(dsMap.get("user"));
itsDs.setPassword(dsMap.get("password"));
return itsDs;
}
#Bean(name = "sfPartsDB")
public JdbcTemplate snowFlakeParts() {
System.out.println("Jdbc template method called.");
DataSource cosmosDs = getPartsDataSource();
JdbcTemplate sfPartsDB = new JdbcTemplate(cosmosDs);
sfPartsDB.setResultsMapCaseInsensitive(true);
return sfPartsDB;
}
#Bean(name = "sfCosmosDB")
public JdbcTemplate snowFlakeCosmos() {
System.out.println("Jdbc template method called.");
DataSource cosmosDs = getCosmosDataSource();
JdbcTemplate sfCosmosDB = new JdbcTemplate(cosmosDs);
sfCosmosDB.setResultsMapCaseInsensitive(true);
return sfCosmosDB;
}
//
#Bean(name = "oracleDB")
public JdbcTemplate oracleTemplate() {
DataSource itsDs = getITSDataSource();
JdbcTemplate oracleDB = new JdbcTemplate(itsDs);
oracleDB.setResultsMapCaseInsensitive(true);
return oracleDB;
}
}
Please try with below code. It works!!
1. #Qualifier to particular datasource reference(by name).
2. #Import Aggregate all the configuration classes.
#Configuration
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:database.properties"})
public class DatabaseConfig {
#Autowired
private Environment env;
#Bean(name="ds")
public DataSource getDataSource(){
System.out.println("++++++++++++++++DATABASE++++++++++++++START++++++++++++");
System.out.println("Method to crate data source");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("net.snowflake.client.jdbc.SnowflakeDriver");
ds.setUrl(env.getRequiredProperty("db.url"));
ds.setUsername(env.getRequiredProperty( "db.username"));
ds.setPassword(env.getRequiredProperty( "db.password"));
return ds;
}
#Bean(name="template")
public JdbcTemplate jdbcTemplate(#Qualifier("ds")DataSource ds) {
System.out.println("Jdbc template method called.");
JdbcTemplate template = new JdbcTemplate(ds);
template.setResultsMapCaseInsensitive(true);
System.out.println("++++++++++++++++DATABASE++++++++++++++END+++++++++++");
return template;
}
}
#Import(DatabaseConfig.class) /* add this line to aggrgate java configuration classes*/
#Configuration
#EnableWebMvc
#ComponentScanenter code here(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:global.properties"})
public class GlobalWebConfig extends WebMvcConfigurerAdapter {
}

Spring JpaConfiguration give an exception on running server

I try to configure a JpaConfiguration to working with jetty and H2 (just for a test).
When I run the server I'm receiving an error. But I don't understand the reason.
I'm using spring-data-jpa.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "my.package.model.repository")
#ComponentScan(basePackages = {"my.package.model.services"})
public class JpaConfiguration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public EntityManagerFactory entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("my.package.model.entity");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory.getObject();
}
#Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
The error is :
2013-11-29 13:36:33.042:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'memberService': Injection o
f autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.package.model.repository.MemberRepository my.package.model.services.MemberServiceImpl.memberRepository; nested exception is org.springframework.beans.f
actory.BeanCreationException: Error creating bean with name 'memberRepository': Cannot create inner bean '(inner bean)' of type [org.springframework.orm.jp
a.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Err
or creating bean with name '(inner bean)#1': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception i
s org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [my/package/config/JpaConfiguration.class]: No matching factory method found: factory bean 'jpaConfiguration'; factory method 'entityManagerFactory()'. Check tha
t a method with the specified name exists and that it is non-static.:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [my/package/config/JpaConfiguration.class]: No matching factory method found: factory bean 'jpaConfiguration'; factory method 'entityManagerFactory()'. Check that
a method with the specified name exists and that it is non-static.
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:536)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.ja
va:1055)
Thanks a lot for your help.
You can do it in a more simple way: you need a single method with LocalContainerEntityManagerFactoryBean as return type. Than, you can inject the EntityManager bean wherever you want without any other fuss.
public class JpaConfiguration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("my.package.model.entity");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory;
}
//remainder configuration for trasnactions and so on
}
#Service
public class Service{
#PersistenceContext(unitName = "test")
private EntityManager em;
}
The problem is coming from my web.xml file.
configuration must content configuration class and not package :
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>my.package.WebConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Importatn line is :
<param-value>my.package.WebConfiguration</param-value>
I wrote only
<param-value>my.package</param-value>

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

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
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.

Categories

Resources