文章目录
摘要
近期由于项目使用mybatis出现了数据源阻塞,导致应用程序假死,服务超时引发严重后果,故此下定决心重新梳理一下spring+mybatis+c3p0整合问题,主要分为:配置、源码(通过一次数据库操作分析)、myabatis缓存、问题总结
使用版本spring 4.1.7.RELEASE、mybatis 3.3.0、mybatis-spring 1.2.3
spring + mybatis + c3p0 整合(配置篇)
通过一次数据库操作分析mybatis
示例代码
@Component
public class Test {
@Autowired
private TestMapper testMapper;
@PostConstruct
public void init(){
testMapper.selectByNo("10001");
}
mybatis执行流程
mapper代理对象MapperProxy
首先spring初始化Test对象时会注入TestMapper,其实是MapperFactoryBean的getObject返回的MapperProxyFactory工厂生成的代理对象MapperProxy。
再调用MapperMethod的execute方法,再通过SqlSessionFactory创建SqlSession执行增删改查详情见上图执行流程。mybatis架构的各个组件后面再详细分析
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
此处的getSqlSession()获取的是其父类SqlSessionDaoSupport的属性
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if(!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public SqlSession getSqlSession() {
return this.sqlSession;
}
再调用SqlSessionTemplate的getMapper方法,getConfiguration此方法其实是从SqlSessionFactory 中获取configuration对象
public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
}
Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
最终调用MapperRegistry的getMapper方法通过MapperProxyFactory工厂类生成代理对象返回
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
到这可能会疑惑,Configuration什么时候创建的,MapperProxyFactory与mapperType的对应关系又是什么时候添加的,这里可以看前一篇配置文件SqlSessionFactoryBean
<!-- mabatis SqlSessionFactory 创建Configuration -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
Spring在初始化SqlSessionFactoryBean时,其实会调用到SqlSessionFactoryBean的getObject方法,可以看到Configuration是在此时创建的
public SqlSessionFactory getObject() throws Exception {
if(this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
XMLConfigBuilder xmlConfigBuilder = null;
Configuration configuration;
if(this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
...省略部分源码
return this.sqlSessionFactoryBuilder.build(configuration);
再将configuration作为参数创建DefaultSqlSessionFactory对象
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
好了,到这里configuration创建搞清楚了,那mapper的键值对又是在什么时候创建的,这个需要回到第一步MapperFactoryBean这里,其父类的父类DaoSupport实现了InitializingBean接口,会在属性设置完成后执行afterPropertiesSet方法,可以看到mapper是在这时调用addMapper方法添加的
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>{
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if(this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
}
DaoSupport类
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
前面多次提到FactoryBean的getObject方法,这里简单说下BeanFactory和FactoryBean区别
BeanFactory是个Factory,也就是IOC容器或对象工厂;FactoryBean是个接口,如实现该接口的MapperFactoryBean其本质是个bean。
在Spring中,实现了该接口的Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。可用简单的理解FactoryBean可以生成BeanFactory。
Spring在初始化实现了该接口的bean时,最终是调用其getObject完成的
spring的mybatis相关bean加载
经过以上分析可能会有个疑惑,myabatis的相关bean是何时交给spring容器管理的;还是得看前文配置文件
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.company.business.mapper" />
<property name="sqlSessionFactory" ref="dataSource"/>
</bean>
MapperScannerConfigurer类
其实现了BeanDefinitionRegistryPostProcessor接口,spring初始化时会调用该类的postProcessBeanDefinitionRegistry方法实现自动注册mybatis相关bean。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
...
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if(this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
ClassPathMapperScanner类
ClassPathMapperScanner调用父类ClassPathBeanDefinitionScanner的doScan方法扫描解析前文配置文件中指定的mapper包扫描路径,解析组装为BeanDefinitionHolder对象列表
ClassPathMapperScanner的registerBeanDefinition方法调用BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry)注册
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if(beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
到此mybatis的相关bean就交给spring容器管理了,在spring初始化bean时,每个mapper都会走一遍创建mapper代理对象MapperProxy的过程。
本文到此也就结束了,下面会逐个分析一下mybatis的几个核心对象
spring初始化部分相关源码
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var5) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt", var5);
this.destroyBeans();
this.cancelRefresh(var5);
throw var5;
}
}
}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
}
#invokeBeanFactoryPostProcessors方法部分代码
BeanDefinitionRegistryPostProcessor pp = (BeanDefinitionRegistryPostProcessor)beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
registryPostProcessors.add(pp);
processedBeans.add(ppName);
pp.postProcessBeanDefinitionRegistry(registry);
reiterate = true;
引用
https://blog.csdn.net/kangsa998/article/details/100565154
https://blog.csdn.net/javageektech/article/details/99518588