I have next applicationContext.xml
file on the root of classpath:
<context:annotation-config />
<context:property-placeholder location="classpath:props/datasource.properties" />
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:url="${jdbc.url}"
p:driverClassName="${jdbc.driverclass}"
p:validationQuery="SELECT sysdate FROM dual" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="datasource"
p:mapperLocations="classpath:mappers/*-mapper.xml" />
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="datasource" />
<bean id="mappeScannerConfigurere" class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:sqlSessionFactory-ref="sqlSessionFactory"
p:basePackage="com.mypackage" />
props/datasource.properties
also exists on the root of classpath with such content:
jdbc.url=myjdbcurl
jdbc.driverclass=myClass
jdbc.username=myUserName
jdbc.password=myPassword
I have a spring managed test where I declare to use previously mentioned applicationContext.xml via next annotations:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
When I invoke test method i get next error from spring:
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverclass}'
As I understand sping didn't resolve reference to jdbc.driverclass. What have I done wrong?
PS: I'm using spring 3.2.3.RELEASE
**
EDIT
**
Perhaps the problem may be in MapperScannerConfigurer
. It is a BeanDefinitionRegistryPostProcessor
and as Javadoc says:
Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in
So MapperScannerConfigurer
instantiates datasource object via sqlSessionFactory with BeanFacoryPostProcessor
(which is responsible for <context:property-placeholder/>
) have not been utilized.
So my question transforms to how to reorder BeanFacoryPostProcessor
from <context:property-placeholder/>
and BeanDefinitionRegistryPostProcessor
(MapperScannerConfigurer
)?
Resolved
After a couple hours of investigation I found the solution:
As I said earlier MapperScannerConfigurer
is a BeanDefinitionRegistryPostProcessor
which fires before BeanFactoryPostProcessor
which is responsible for <context:property-placeholder/>
. So, during the creation of MapperScannerConfigurer references to external properties will not be resolved. In this case we have to defer the creation of datasource to the time after BeanFactoryPostProcessor
have been applied. We can do that in several ways:
- remove
p:sqlSessionFactory-ref="sqlSessionFactory"
fromMapperScannerConfigurer
. In this case datasource object will not be created beforeMapperScannerConfigurer
, but afterBeanFactoryPostProcessor
which is responsible for<context:property-placeholder/>
. If you have more than one sqlSessionFactory in applicationContext, than can be some troubles - In versions of mybatis-spring module higher than 1.0.2 there is a possibility to set
sqlSessionFactoryBeanName
instead ofsqlSessionFactory
. It helps to resolve PropertyPlaceHolder issue withBeanFactoryPostProcessor
. It is a recommended way to solve this issue described in mybatis-spring doc