spring + mybatis + c3p0 整合(源码分析)

摘要

近期由于项目使用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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值