报错日志
在搭建 spring 4.3 + mybatis 3.4 的开发框架时, 启动报错
启动报错如下:
nested exception is java.sql.SQLException: ${jdbc.driver}
配置都是大同小异, 唯一的区别是加了这个更便捷的配置
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.taro.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
有了这个配置就不需要再写一大堆繁琐的 Dao 配置, spring 会自动为我们把这些 dao 写入到 内部维护的 BeanDefinitions 中去.
解决方法
一. 如果有配置 default-autowire 将这个配置去掉
二. spring 配置文件中 org.mybatis.spring.SqlSessionFactoryBean 的命名不要命名成 sqlSessionFactory , 另外
MapperScannerConfigurer
中的
sqlSessionFactoryBeanName
也改成相应的名字 正确的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName" default-lazy-init="true">
<!-- DataSource 数据 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="name" value="souchecar"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="20"/>
<property name="minIdle" value="2"/>
<property name="initialSize" value="2"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="defaultAutoCommit" value="true"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="6000"/>
<property name="logAbandoned" value="true"/>
<property name="filters" value="stat"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="mapperLocations">
<list>
<value>classpath*:sqlmap/**/*.xml</value>
</list>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- DAO 接口所在包名, Spring 会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.taro.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
分析原因
看报错可知配置信息没有加载进去
配置信息我是配置了的, 配置没有任何问题
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:*.properties</value>
</list>
</property>
</bean>
那么是什么原因呢, 让我们来看源码
可知
PropertyPlaceholderConfigurer
是实现了
BeanFactoryPostProcessor
接口的
而
MapperScannerConfigurer
实现了
BeanDefinitionRegistryPostProcessor
接口
要想知道这几个接口含义, 以及是在 spring 加载的哪个过程执行的, 还是得一步步的看源码
回到 spring 最经典的入口代码
@Override public void refresh() throws BeansException,
IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 一, Prepare this context for refreshing.
prepareRefresh();
// 二, 重启容器, 并载入 BeanDefinitions
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 三, Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 四, Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 五, 执行 BeanFactoryPostProcessor 接口和 BeanDefinitionRegistryPostProcessor 接口的地方
invokeBeanFactoryPostProcessors(beanFactory);
// 六, Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// 七, Initialize message source for this context.
initMessageSource();
// 八, Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 九, Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch(BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization -" + "cancelling refresh attempt:" + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
我们重点来看第 5 步, 进入 invokeBeanFactoryPostProcessors 方法
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
invokeBeanFactoryPostProcessors public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List < BeanFactoryPostProcessor > beanFactoryPostProcessors) {
// 先执行 BeanDefinitionRegistryPostProcessors // processedBeans 存放已经执行过的 FactoryPostProcessor
Set < String > processedBeans = new HashSet < String > ();
// 先判断 beanFactory 是不是 BeanDefinitionRegistry 类型
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List < BeanFactoryPostProcessor > regularPostProcessors = new LinkedList < BeanFactoryPostProcessor > ();
List < BeanDefinitionRegistryPostProcessor > registryProcessors = new LinkedList < BeanDefinitionRegistryPostProcessor > ();
// 把手动加入的 BeanFactoryPostProcessor 分类型分别放入 regularPostProcessors ,registryProcessors 中
for (BeanFactoryPostProcessor postProcessor: beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
} else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
// 下面的代码会根据优先级情况先后执行 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessors
List < BeanDefinitionRegistryPostProcessor > currentRegistryProcessors = new ArrayList < BeanDefinitionRegistryPostProcessor > ();
// 先执行实现了 PriorityOrdered (优先执行) 的接口的 BeanDefinitionRegistryPostProcessor.class
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName: postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 再执行 Ordered 类型的 BeanDefinitionRegistryPostProcessor.class
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName: postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 最后执行 剩下的 BeanDefinitionRegistryPostProcessor
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName: postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// 执行手动加入的
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else {
// 直接执行
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 根据排序顺序执行 BeanFactoryPostProcessor , 前面已经执行过的 BeanDefinitionRegistryPostProcessor 不会执行
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List < BeanFactoryPostProcessor > priorityOrderedPostProcessors = new ArrayList < BeanFactoryPostProcessor > ();
List < String > orderedPostProcessorNames = new ArrayList < String > ();
List < String > nonOrderedPostProcessorNames = new ArrayList < String > ();
for (String ppName: postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
} else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
} else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List < BeanFactoryPostProcessor > orderedPostProcessors = new ArrayList < BeanFactoryPostProcessor > ();
for (String postProcessorName: orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List < BeanFactoryPostProcessor > nonOrderedPostProcessors = new ArrayList < BeanFactoryPostProcessor > ();
for (String postProcessorName: nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
超级长的一堆代码, 看着慌...
我们分几步来分析
BeanDefinitionRegistryPostProcessor 优先执行, 执行顺序 实现了么接口 PriorityOrdered
最优先 Ordered 其次 , 最后执行普通的 BeanDefinitionRegistryPostProcessor
再执行手动加入的 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor (由容器的 addBeanFactoryPostProcessor
方法加入的)
之后执行 BeanFactoryPostProcessor , 执行顺序与 BeanDefinitionRegistryPostProcessor 类似
现在我们再来分析原因
通过查看
MapperScannerConfigurer
源码, 可知其实现了
BeanDefinitionRegistryPostProcessor
接口, 而
PropertyPlaceholderConfigurer
实现的是
BeanFactoryPostProcessor
接口. 那么这就说明
MapperScannerConfigurer
是优先于
PropertyPlaceholderConfigurer
执行的, 那么要是
MapperScannerConfigurer
提前初始化了数据库连接, 而相关配置属性还没被替换, 就会报这个错了.
貌似问题找打了, 可仔细一看, 我
MapperScannerConfigurer
配置的注入属性不是 sqlSessionFactoryBeanName 吗, 这可只是 String 类型, 怎么就提前去初始化了 sqlSessionFactory 呢
这就得从 spring 的依赖注入规则去分析了
首先, 我配置的是自动注入
default-autowire="byName"
意思就是说就算我不配各种 property Spring 也会自动根据 Name 去容器里找然后注入给我. 那么问题就出在这里, 因为
MapperScannerConfigurer
里有 SqlSessionFactory 类型的属性, 所以 spring 就自作主张给我们去容器里找根据名字 sqlSessionFactory 去找这个类型的 BeanDefinition, 因为我们配置的 sqlSessionFactory 名字恰好也是 sqlSessionFactory, 结果就给找到了, 一找到就立马调用 getBean 方法提前初始化 sqlSessionFactory 了, 进而提前初始化了 dataSource, 导致了报错!
所以解决方法就是上文提到的这两种方式
来源: http://www.jianshu.com/p/ff38d96a18a7