我正在尝试在Spring中运行DAO类的TestNG测试。但是,尽管有注释,DataSource引用仍未自动装配。以下是测试的一部分:
@ContextConfiguration(locations={"classpath:WEB-INF/servlet-context.xml"})
public class ToDoDaoImplTest extends AbstractTestNGSpringContextTests {
@Autowired
// Construction of this object fails
private ToDoItemDaoImpl toDoDao;
}
这是我的Spring配置:
<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课程:
@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;
}
这是错误的堆栈跟踪:
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
显示已创建dataSource的部分日志:
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'
这让我很困惑。已创建dataSource对象,但未将其自动装入Spring管理的对象中。我做错了什么?
答案 0 :(得分:5)
问题在这里
public ToDoItemDaoImpl() {
// dataSource is null here
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
Spring只能在创建对象后自动装配字段。这在构造函数完成后发生。
Spring将使用反射,即。像Class.forName(yourClass).newInstance()
这样的东西,用来创建你的bean,然后再用反射来设置每个属性。
但是,在构造函数中,该字段仍为null
,因为这是所有未初始化的引用类型字段的默认值。
一种解决方案是将构造函数留空并添加@PostConstruct
带注释的方法
@PostConstruct
public void init() {
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
另一种解决方案是在setDataSource()
方法中添加初始化。
答案 1 :(得分:1)
除了上面提到的Sotirios之外,您还可以使用Spring的contructor-args依赖注入来初始化dataSource。因此,您可以将构造函数修改为:
public ToDoItemDaoImpl(DataSource dataSource) {
this.dataSource = dataSource;
insert = new SimpleJdbcInsert(dataSource).withTableName("toDoItem").usingGeneratedKeyColumns("id");
}
然后您还需要在bean配置文件中添加以下行:
<bean id="toDoItemDaoImpl" class="mypackage.ToDoItemDaoImpl">
<constructor-arg ref="dataSource"/>
</bean>