8

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 BeanFactoryPostProcessorhave been applied. We can do that in several ways:

  • remove p:sqlSessionFactory-ref="sqlSessionFactory" from MapperScannerConfigurer. In this case datasource object will not be created before MapperScannerConfigurer, but after BeanFactoryPostProcessor 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 of sqlSessionFactory. It helps to resolve PropertyPlaceHolder issue with BeanFactoryPostProcessor. It is a recommended way to solve this issue described in mybatis-spring doc
aristotll
  • 8,694
  • 6
  • 33
  • 53
maks
  • 5,911
  • 17
  • 79
  • 123
  • Is there any other `PropertyPlaceholderConfigurer` in your Spring context configuration? [see this thread](http://stackoverflow.com/questions/4779572/could-not-resolve-spring-property-placeholder) – LaurentG Jul 07 '13 at 14:33
  • definitely no, I checked it by setting breakpoint in setLoctions method: it is called only once – maks Jul 07 '13 at 14:37
  • I would try with a leading `/` for the properties location: `location="classpath:/props/datasource.properties"` –  Jul 07 '13 at 16:31
  • i have the same issue,and i just remove the autoWire=“byName",and the properties file work. – Mr Lou Aug 21 '13 at 09:13
  • 1
    @maks you ripper. I had encountered the same issue that as soon as I added `MapperScannerConfigurer` it would screw with my property placeholders. When looking at the code I found that it implements `BeanDefinitionRegistryPostProcessor` which is what lead me to believe it to be the cause and ultimately found your question. Answer your own question, man! This is the exact solution necessary :) – Brett Ryan May 25 '16 at 15:48

2 Answers2

0

I was having the same issue and came across this post but I was unable to resolve it the same way maks did. What ended up working for me was to set the ignoreUnresolvablePlaceholders property value to true.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
        <value>classpath:database.properties</value>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>

I am using Spring 3.2.3.RELEASE as well. I realize this post is over 4 months old but I figured someone might find it useful.

Dan
  • 2,701
  • 1
  • 29
  • 34
0

Short form: What is the proper way to load an implementation of: BeanDefinitionRegistryPostProcessor?

Expanded form: Is there a way to load BeanDefinitionRegistryPostProcessor before any beans have been created. If you look at the javadoc:

Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in.

So it's meant to be loaded when bean definitions have been created but before any beans have been created. If we just create it as a regular bean in the application xml then it defeats the purpose of having this bean in the first place.

VIDIMI
  • 1