I am trying to run TestNG tests for DAO class in Spring. But DataSource reference is not autowired despite of the annotation. Here is part of the test:
@ContextConfiguration(locations={"classpath:WEB-INF/servlet-context.xml"})
public class ToDoDaoImplTest extends AbstractTestNGSpringContextTests {
@Autowired
// Construction of this object fails
private ToDoItemDaoImpl toDoDao;
}
Here is my Spring configuration:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- DataSource to be injected -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:test" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
<jdbc:script location="classpath:db.create.sql" />
</jdbc:initialize-database>
<context:component-scan base-package="org.myapp"/>
DAO class:
@Repository
public class ToDoItemDaoImpl implements ToDoItemDao {
private DataSource dataSource;
private SimpleJdbcInsert insert;
public ToDoItemDaoImpl() {
// dataSource is null here
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
@Autowired
@Qualifier("dataSource")
// This method is not called by Spring
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
Here is stacktrace of the error:
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.myapp.dao.ToDoItemDaoImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1004)
... 50 more
Caused by: java.lang.IllegalArgumentException: Property 'dataSource' is required
at org.springframework.jdbc.support.JdbcAccessor.afterPropertiesSet(JdbcAccessor.java:134)
at org.springframework.jdbc.core.JdbcTemplate.<init>(JdbcTemplate.java:165)
at org.springframework.jdbc.core.simple.AbstractJdbcInsert.<init>(AbstractJdbcInsert.java:97)
at org.springframework.jdbc.core.simple.SimpleJdbcInsert.<init>(SimpleJdbcInsert.java:60)
at org.myapp.dao.ToDoItemDaoImpl.<init>(ToDoItemDaoImpl.java:33)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
... 52 more
null
Part of the log that shows that dataSource is created:
DEBUG - DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource'
DEBUG - DefaultListableBeanFactory - Creating instance of bean 'dataSource'
DEBUG - DefaultListableBeanFactory - Eagerly caching bean 'dataSource' to allow for resolving potential circular references
INFO - DriverManagerDataSource - Loaded JDBC driver: org.h2.Driver
DEBUG - DefaultListableBeanFactory - Finished creating instance of bean 'dataSource'
DEBUG - DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.jdbc.datasource.init.DataSourceInitializer#0'
DEBUG - DefaultListableBeanFactory - Creating instance of bean 'org.springframework.jdbc.datasource.init.DataSourceInitializer#0'
DEBUG - DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.jdbc.datasource.init.DataSourceInitializer#0' to allow for resolving potential circular references
DEBUG - DefaultListableBeanFactory - Returning cached instance of singleton bean 'dataSource'
This is confuses me. The dataSource object is created, but is not Autowired into a Spring-managed object. What am I doing wrong?
The issue is here
public ToDoItemDaoImpl() {
// dataSource is null here
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
Spring can only autowire fields after the object has been created. That happens after the constructor is finished.
Spring will use reflection, ie. something like Class.forName(yourClass).newInstance()
, to create your bean and then again use reflection to set each property.
However, in your constructor, the field is still null
as that is the default value for all uninitialized fields of reference types.
One solution is to leave the constructor empty and add a @PostConstruct
annotated method
@PostConstruct
public void init() {
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
Another solution is to add that initialization inside the setDataSource()
method.
Besides what Sotirios suggested above, you can also use Spring's contructor-args dependency injection to initialize the dataSource. So you can modify the constructor to be:
public ToDoItemDaoImpl(DataSource dataSource) {
this.dataSource = dataSource;
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
Then you also need to add the following lines in the bean configuration file:
<bean id="toDoItemDaoImpl" class="mypackage.ToDoItemDaoImpl">
<constructor-arg ref="dataSource"/>
</bean>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.