context:property-placeholder doesn't resolve r

2019-02-06 21:31发布

问题:

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

回答1:

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.



回答2:

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.