【源码】-MyBatis-如何系统地看源码

写在前面

  前段时间做过一个项目,期间用到了动态数据源dynamic-datasource,经历了dbcp2的数据库连接池没有生效到排查定位、MyBatis多种数据库产品兼容、手写MyBatis拦截器等事情。

  花费了好久,一直在打磨这篇文章(不知道花费这么长时间写文章有没有意义,但互联网总得留下点儿什么吧~)。最终,千呼万唤始出来。本文就从源码的角度来系统地看看原理是什么,能学到些什么。如有说的不正确的地方,欢迎指正。


目录

  • 写在前面
  • 一、环境说明
  • 二、为什么?如何做
  • 三、动态数据源部分
    • (一)动态数据源加载、连接池创建
  • 四、MyBatis部分
    • (一) MyBatis核心组件加载
    • (二)MapperProxy初始化
    • (三)连接池的使用、数据库厂商加载、拦截器生效
  • 五、收获
  • 六、附录(plantuml脚本)
    • activity_baomidou_dynamicdatasource.puml
    • activity_mybatis_sqlsessionfactorybean.puml
    • activity_mybatis_mapperproxy.puml
    • activity_mybatis_conn_interceptor.puml
    • class_baomidou.puml
    • class_mybatis.puml
  • 写在后面
  • 系列文章


一、环境说明

名称说明
mybatis版本mybatis-3.4.6.jar
mybatis-spring版本mybatis-spring-1.3.1.jar
mybatis-spring-boot版本mybatis-spring-boot-autoconfigure-1.3.0.jar
dynamic-datasource-spring-boot-starter版本dynamic-datasource-spring-boot-starter-3.5.2.jar
commons-dbcp2版本commons-dbcp2-2.8.0.jar
IDEA编辑器2019
PlantUML插件IDEA / VsCode插件

二、为什么?如何做

说正题前,我们先思考一下,为什么要看源码?

我想可能有几种场景:

1、出问题了,不得不看。
比如在项目中引入了什么包、配置或者做了什么改动,导致项目无法启动或者报错了
2、求知欲。有疑问,带着问题看。
比如本文将要讨论的:dbcp2连接池是什么时机创建的?如何创建的?又是怎么使用的?MyBatis拦截器插件是如何生效的?等等
3、面试。 不过比起八股文,自己研究一下体会会更深。

那看源码,我们能收获什么?
个人认为看源码可以去切实地体会优秀的代码设计,了解高手是怎么做的,包括设计模式的运用、扩展点、设计原则等等。思考如果自己以后遇到类似问题该如何运用。不熟悉设计模式这部分内容的朋友,可以去参考这篇文章【GitHub】- design-pattern(设计模式)

另外看源码,笔者还有一个提示:不要一下子要求全搞懂,否则你会越陷越深… 看到最后,不知所云。根据当下的水平,逐步丰富自己的体系,建议带着问题,带着疑问,点到为止(问题搞懂,不要无限蔓延)

那究竟该如何看?这里我谈一谈自己的见解(正文内容也是这么做的)。

稍微扩展一下,延展到如何学习一个新的框架或者新的知识点
1、通读官方文档、GitHub的README
知道这个框架是做什么的,主要用于解决什么问题,了解框架的架构图
2、结合官方Demo,熟悉基本操作API
3、自己动手
写Demo,本地调试。梳理流程,主要包括2部分内容:类图和活动图
画类图,能让你快速全面地了解到这个类是干什么用的,有什么样的体系(继承、实现、依赖以及关联关系)
画活动图,能让你知道这些类的调用关系、这些类是如何相互作用使用的。

不熟悉类图和活动图的这部分朋友,可以去参考这篇文章【UML】- 类图

看源码,首先看什么能最快地找到突破口?

我认为,
首先看它的pom.xml(看依赖关系,pom中有什么jar包可能就会具备哪些功能)
其次,看源码的包命名(优秀的代码内聚做的比较好)
然后,从resource开始着手(可能有一些配置需要提前了解)

好了,接下里,我们回归正题。
以下从2个部分开始说,主要包括动态数据源和MyBatis。


三、动态数据源部分

在这里插入图片描述

(一)动态数据源加载、连接池创建

首先找到 dynamic-datasource-spring-boot-starter-3.5.2.jar
我们在resource下找到了META-INF/spring.factories,咦,这是什么?这不就是自动装配么。好了,找到切入口了。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration

看看 DynamicDataSourceAutoConfiguration 干了什么事情

# DynamicDataSourceAutoConfiguration
1.将 yml 中的配置信息(数据源url/driver、连接池等等配置信息)构建成Bean,加入到Spring容器
2.通过 @Import 导入creator的自动配置类 DynamicDataSourceCreatorAutoConfiguration,进行creator初始化2.1 创建一个 DefaultDataSourceCreator(List<DataSourceCreator> dataSourceCreators),构造注入dataSourceCreators,与DataSourceCreator绑定关系(也就是dbcp2的creator)2.2 判断classpath中有dbcp2的包,创建 Dbcp2DataSourceCreator
3.创建 YmlDynamicDataSourceProvider()3.1 通过构造方法和 dataSourcePropertiesMap 进行了绑定3.1 通过继承,属性注入 DefaultDataSourceCreator,和creator进行了绑定
4.创建 DynamicRoutingDataSource 路由4.1 @Autowired 属性注入 provider,也就是 YmlDynamicDataSourceProvider4.2 实现了InitializingBean,初始化Bean时,会调用@afterPropertiesSet

我们先梳理一下这里边的几个绑定关系:
DynamicRoutingDataSource 路由包含一个 YmlDynamicDataSourceProvider,
YmlDynamicDataSourceProvider 中有 dataSourcePropertiesMap 和 DefaultDataSourceCreator,
DefaultDataSourceCreator 中有 List,包含了dbcp的creator。

该初始化的进行初始话,该绑定的关系的绑定关系。
接下来我们重点看 DynamicRoutingDataSource#afterPropertiesSet()
动态数据源加载的过程,也就是连接池的创建过程

# DynamicRoutingDataSource#afterPropertiesSet()
1.调用 provider#loadDataSources()# YmlDynamicDataSourceProvider
2.调用 defaultDataSourceCreator#createDataSource# DefaultDataSourceCreator
3.调用 dbcp2#doCreateDataSource(dataSourceProperty)# BasicDataSource
4.调用 dbcp2的连接池去创建数据源 dataSource#start()4.1 for循环配置文件中的 initialSize,connectionPool.addObject() 添加到LinkedBlockingDeque中

至此,连接池就创建完毕了。

以下是活动图和类图,
温馨提示:鼠标右键-》在新标签页中打开图片可查看高清图。PlantUML脚本在附录部分
在这里插入图片描述
在这里插入图片描述

有了类图就很明显了,这里面有3个体系:

DataSource(spring实现了AbstractDataSource)
DataSourceCreator
DynamicDataSourceProvider

相应地对应了有3个抽象类:

1.AbstractRoutingDataSource
这个类在连接池创建的时候没用到,这里也说一下,在获取时,为重载DataSource#getConnection 添加一个determineDataSource()获取连接池的操作,
具体实现在DynamicRoutingDataSource中2.AbstractDataSourceCreator
抽象连接池创建器,这里边抽象出一个 doCreateDataSource(DataSourceProperty dataSourceProperty)由具体的实现类dbcp2、druid、Hikari实现
另外,在doCreateDataSource前后做了一些通用逻辑
// 源码
dataSourceInitEvent.beforeCreate(dataSourceProperty)
DataSource dataSource = doCreateDataSource(dataSourceProperty);
dataSourceInitEvent.afterCreate(dataSource)3.AbstractDataSourceProvider
多数据源加载接口,默认的实现为从yml信息中加载所有数据源。
抽象出接口,就可以扩展一些其他加载数据源的方式

四、MyBatis部分

(一) MyBatis核心组件加载

在这里插入图片描述

首先找到 mybatis-spring-boot-autoconfigure-1.3.0.jar
在resource下找到了META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

看一下MybatisAutoConfiguration做了什么事情。

# MybatisAutoConfiguration
1.@AutoConfigureAfter,优先注入datasource数据源
2.通过datasource数据源,构造方式 SqlSessionFactoryBean,绑定了数据源2.1 设置datasource、configuration2.2 由于实现了InitializingBean,初始化Bean时,会调用@afterPropertiesSet2.2.1 处理configuration、添加Interceptor、databaseId、创建事务工厂、解析xml对象,最后构建一个DefaultSqlSessionFactory
3.通过sqlsesssionfactory创建sqlSessionTemplate3.1 构造方法中隐藏了一个sqlSessionProxy代理,它代理了SqlSession接口,通过SqlSessionInterceptor的invoke实现逻辑

说明:这里SqlSessionTemplate定义了一套操作Mybatis的模板(实际上方法的执行调用的都是SqlSession的实现类方法)

因为框架想在操作完方法之后处理一些事务的提交、session的关闭等操作(实现方式是使用代理Proxy,代理SqlSession)

以下是活动图:
温馨提示:鼠标右键-》在新标签页中打开图片可查看高清图。PlantUML脚本在附录部分
在这里插入图片描述

(二)MapperProxy初始化

在这里插入图片描述

我们知道接口是无法执行方法的,这里MyBatis把所有的mapperInterface代理成了MapperProxy。以下是MapperProxy的创建过程活动图:
温馨提示:鼠标右键-》在新标签页中打开图片可查看高清图。PlantUML脚本在附录部分
在这里插入图片描述

(三)连接池的使用、数据库厂商加载、拦截器生效

在这里插入图片描述

以下是MyBatis操作整体流程活动图,
温馨提示:鼠标右键-》在新标签页中打开图片可查看高清图。PlantUML脚本在附录部分
在这里插入图片描述

我们以一个示例 baseMapper.queryByList(map) 作为入口来说明,
执行baseMapper#queryByList()实际上调用的是 mapperproxy#invoke(前面提到了mapperproxy代理了baseMapper)

mapperproxy调用了mapperMethod#execute(sqlSession, args)

# MapperMethod
1.根据 SqlCommandType 执行SQL,也就是判断 CRUD,然后通过SqlSession做相应操作这里的重点来了,SqlSession现在是什么?SqlSession是SqlSessionTemplate,也就是会调用SqlSessionTemplate#selectList
在源码里你会看到接着调用的是 sqlSessionProxy#selectList,sqlSessionProxy我们前面提到过是代理的SqlSession,InvokeHandler是SqlSessionInterceptor
那么,此时就会调用SqlSessionInterceptor#invoke# SqlSessionInterceptor
2.这是一个模板的方法,进行了3个操作2.1 获取真正要处理的 getSqlSession2.2 执行方法2.3 判断事务操作,提交事务2.4 关闭session接下来详细说一下[2.1] getSqlSession()干了什么事情,它通过前面创建的 DefaultSqlSessionFactory 创建一系列的组件。
# DefaultSqlSessionFactory
2.1.1根据datasource创建tx事务,transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit)
2.1.2.创建Executor执行器,configuration.newExecutor(tx, execType)2.1.2.1 这个executor执行器会经过拦截器链(分页、自定义的属性填充)的包装,这里又是一个Plugin的代理,会代理executor里面的方法,在真正方法执行前,先执行这些拦截器的操作2.1.2.2 最终返回一个DefaultSqlSession对象当执行[2.2] 的方法时,通过反射的方式调用就是DefaultSqlSession#selectList
#DefaultSqlSession
2.2.1 这里边会去获取到databaseId的sql
2.2.2 先去执行拦截器链中的拦截器(分页、属性填充)
2.2.3 要获取Connection2.2.3.1 从事务中获取datasource,此时的datasource在DynamicRoutingDataSource的datasourceMap中2.2.3.2 拿到datasource后(dbcp2的datasource),会调用getConnection,从 LinkedBlockingDeque#pollXxx()

以下是MyBatis的类图,
看看类图,你至少知道什么是datasource、什么是connection、什么是sqlsession,对他们的关系有了进一步的理解了吧。
温馨提示:鼠标右键-》在新标签页中打开图片可查看高清图。PlantUML脚本在附录部分

在这里插入图片描述


五、收获

  • 面向接口编程,抽象类实现通用逻辑,也可以通过模板的方式实现通用逻辑
  • 一些自动配置,我们可以通过starter自动装配实现,关系的构建可以通过Bean初始化,去实现InitializingBean,进而通过属性或者构造方法的方式去注入一些你需要的Bean
  • 可以通过注入Event的这种方式去做一个before和after的事情,也可以通过代理的方式实现
  • 优秀名的命名、编码风格

六、附录(plantuml脚本)

activity_baomidou_dynamicdatasource.puml

@startuml
skinparam style strictuml
skinparam sequenceMessageAlign direction
skinparam roundcorner 20
skinparam sequenceParticipant underline
autoactivate ontitle 【动态数据源加载及连接池创建】活动图'baomidou
participant ddsa_config << (C,#ADD1B2) DynamicDataSourceAutoConfiguration >>
note over of ddsa_config #aqua自动装配,核心Bean创建的入口
end noteparticipant dds_properties << (C,#ADD1B2) DynamicDataSourceProperties >>
participant ds_property << (C,#ADD1B2) DataSourceProperty >>
note over dds_properties, ds_propertyyml属性对应的Bean(内外层)
end noteparticipant dds_creator_a_config << (C,#ADD1B2) DynamicDataSourceCreatorAutoConfiguration >>
note over of dds_creator_a_configcreator自动配置
end noteparticipant ads_creator << (A,#A8DEDF) AbstractDataSourceCreator >>
participant dds_creator << (C,#ADD1B2) DefaultDataSourceCreator >>
participant dbcp2ds_creator << (C,#ADD1B2) Dbcp2DataSourceCreator >>
note over dds_creator_a_config, dbcp2ds_creatorcreator体系
end noteparticipant ads_provider << (A,#A8DEDF) AbstractDataSourceProvider >>
participant ydds_provider << (C,#ADD1B2) ymlDynamicDataSourceProvider >>
note over of ydds_provider #aqua组件的关系绑定
end note
note over ads_provider, ydds_providerprovider体系
end noteparticipant drds << (C,#ADD1B2) DynamicRoutingDataSource >>
note over of drds #aqua动态数据源加载入口
end note'dbcp2
participant basic_ds << (C,#ADD1B2) BasicDataSource >>
note over of basic_dsdbcp2连接池
end note-> ddsa_config: dynamic-datasource-spring-boot-starter-3.5.2.jar 自动装配 META-INF/spring.factories\n EnableAutoConfiguration=c.b.d.d.s.b.autoconfigure.DynamicDataSourceAutoConfiguration
==yml配置文件的加载以及初始化==group yml配置构建成Bean,加入spring容器ddsa_config -> dds_properties: @EnableConfigurationPropertiesdds_properties -> ds_property: private Map<String, DataSourceProperty> datasource \n = new LinkedHashMap<>()ds_property --> ddsa_config: yml填充后加入spring容器
endgroup @Import导入creatorddsa_config -> dds_creator_a_config: @Importgroup creator初始化group 创建默认的creatordds_creator_a_config -> dds_creator_a_config: dataSourceCreator(List<DataSourceCreator> dataSourceCreators)\n<color red> 与DataSourceCreator(dbcpCreator)绑定关系dds_creator_a_config -> dds_creator: new DefaultDataSourceCreator()dds_creator --> dds_creator_a_config: <color red>提供了createDataSource(dataSourceProperty)方法endgroup 创建dbcp2的creatordds_creator_a_config -> dds_creator_a_config: dbcp2DataSourceCreator()dds_creator_a_config -> dbcp2ds_creator: new Dbcp2DataSourceCreator()dbcp2ds_creator --> dds_creator_a_config: <color red>提供了 doCreateDataSource(DataSourceProperty dataSourceProperty) 方法endgroup 创建其他的creator(@ConditionalOnClass(BeeDataSource.class))dds_creator_a_config -> dds_creator_a_config: ...endenddds_creator_a_config --> ddsa_config: DynamicDataSourceCreatorAutoConfiguration 被创建
endgroup 构造方法 注入 DynamicDataSourceProperties 和 List<DynamicDataSourcePropertiesCustomizer>ddsa_config -> ddsa_config: DynamicDataSourceAutoConfiguration(properties, dataSourcePropertiesCustomizers)\n <color red>目的是拿到yml中的配置为其他Bean传参
endgroup 创建YmlDynamicDataSourceProviderddsa_config -> ydds_provider : ymlDynamicDataSourceProvider(),\n new对象把datasource这个Map(每一个数据源)作为构造参数传入, <color red>相当于和dataSourcePropertiesMap进行了绑定ydds_provider -> ads_provider: @Autowigreen DefaultDataSourceCreator defaultDataSourceCreator\n属性注入<color red>相当于和creator进行了绑定ads_provider --> ydds_providerydds_provider --> ddsa_config: <color red>提供loadDataSources()方法
endgroup 创建 DynamicRoutingDataSource 路由ddsa_config -> drds: dataSource(),\n new对象,依据DynamicDataSourceProperties的属性设置自身,比如primary属性group @Autowired属性注入providersdrds -> drds: @Autowired List<DynamicDataSourceProvider> providers: <color red>此处的provider就是创建YmlDynamicDataSourceProviderenddrds --> ddsa_config: <color red>由于实现了InitializingBean,提供 afterPropertiesSet() 方法==连接池创建==group #EEE 实现了InitializingBean,重写afterPropertiesSetdrds -> drds #aqua: @Override afterPropertiesSetdrds -> ydds_provider: provider.loadDataSources()ydds_provider -> ads_provider: createDataSourceMap(dataSourcePropertiesMap)ads_provider -> dds_creator: createDataSource(DataSourceProperty dataSourceProperty)dds_creator -> ads_creator: createDataSource(DataSourceProperty dataSourceProperty)ads_creator -> ads_creator: <color red> __abstract doCreateDataSource(dataSourceProperty)__\n <color red>由连接池实现,以dbcp2为例ads_creator -> dbcp2ds_creator: doCreateDataSource(DataSourceProperty dataSourceProperty)dbcp2ds_creator -> basic_ds: start()group #EEE 真正创建连接池basic_ds -> basic_ds: createDataSource()basic_ds -> basic_ds: <color red>for循环连接池initialSize\n <color red>connectionPool.addObject()\r <color red>添加到LinkedBlockingDeque中endbasic_ds --> drdsend
end@enduml

activity_mybatis_sqlsessionfactorybean.puml

@startuml
skinparam style strictuml
skinparam sequenceMessageAlign direction
skinparam roundcorner 20
skinparam sequenceParticipant underline
autoactivate ontitle 【MyBatis核心组件加载】活动图
'ibatis
participant mba_config << (C,#ADD1B2) MybatisAutoConfiguration >>
note over of mba_config #aqua自动装配,核心Bean创建的入口
end note'spring
participant dsa_config << (C,#ADD1B2) DataSourceAutoConfiguration >>participant ssf_bean << (C,#ADD1B2) SqlSessionFactoryBean >>
note over of ssf_bean #aqua构建SqlSessionFactory的入口
end note'mybatis
participant ss_template << (C,#ADD1B2) SqlSessionTemplate >>participant ssf_builder << (C,#ADD1B2) SqlSessionFactoryBuilder >>
participant dss_factory << (C,#ADD1B2) DefaultSqlSessionFactory >>
note over ssf_bean, dss_factorySqlSession相关
end note-> mba_config: mybatis-spring-boot-autoconfigure-1.3.0.jar 自动装配 META-INF/spring.factories\n EnableAutoConfiguration=o.m.s.b.autoconfigure.MybatisAutoConfiguration
==加载yml配置文件及初始化==group 优先注入 DataSourceAutoConfigurationmba_config -> dsa_config: @AutoConfigureAfterdsa_config --> mba_config
endgroup 构造方法方式 给私有属性赋值mba_config -> mba_config: MybatisProperties\n ObjectProvider<Interceptor[]>\n ResourceLoader\n ObjectProvider<DatabaseIdProvider>\n ObjectProvider<List<ConfigurationCustomizer)
endgroup #EEE 创建 SqlSessionFactory(DataSource dataSource)mba_config -> mba_config: sqlSessionFactory(DataSource dataSource), new对象,创建 SqlSessionFactoryBean\n//设置setDataSource、setConfiguration、\n//setPlugins、setDatabaseIdProvider、setMapperLocations(解析classpath:/mapper/*Mapper.xml)mba_config -> ssf_bean: getObject()group #EEE 创建SqlSessionFactoryssf_bean -> ssf_bean #aqua: @afterPropertiesSet()ssf_bean -> ssf_bean: buildSqlSessionFactory()\n<color red>//处理configuration\n<color red>//addInterceptor(plugin)\n<color red>//setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource))\n<color red>//创建SpringManagedTransactionFactory\n<color red>//xmlMapperBuilder.parse();\nbuilderAssistant.addMappedStatement(databaseId)\ncreateSqlSource():SqlSourcessf_bean -> ssf_builder: this.sqlSessionFactoryBuilder.build(configuration)ssf_builder -> ssf_builder: build(config)ssf_builder -> dss_factory: <color red>new DefaultSqlSessionFactory(config)dss_factory --> ssf_beanendssf_bean --> mba_config
endgroup 创建 SqlSessionTemplatemba_config -> mba_config: sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)mba_config -> ss_template: new SqlSessionTemplate()group #EEE 构造方法ss_template -> ss_template: SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, \nExecutorType executorType,\nPersistenceExceptionTranslator exceptionTranslator)\n<color red>//隐藏了一个代理\nthis.sqlSessionProxy = (SqlSession) newProxyInstance(\nSqlSessionFactory.class.getClassLoader(),\nnew Class[] { SqlSession.class },\nnew SqlSessionInterceptor());endss_template --> mba_config
end@enduml

activity_mybatis_mapperproxy.puml

@startuml
skinparam style strictuml
skinparam sequenceMessageAlign direction
skinparam roundcorner 20
skinparam sequenceParticipant underline
autoactivate ontitle 【MyBatis MapperProxy 初始化】活动图
'spring
participant ab_factory << (C,#ADD1B2) AbstractBeanFactory >>participant mf_bean << (C,#ADD1B2) MapperFactoryBean >>
participant ss_template << (C,#ADD1B2) SqlSessionTemplate >>participant config << (C,#ADD1B2) Configuration >>
participant mregistry << (C,#ADD1B2) MapperRegistry >>participant mp_factory << (C,#ADD1B2) MapperProxyFactory >>
participant mproxy << (C,#ADD1B2) MapperProxy >>-> ab_factory: 项目启动,service 注入 dao
== MapperProxy 的初始化==
ab_factory -> ab_factory: getBean(String name, Class<T> requiredType)\n入参示例:("configPropertiesDao", com.zhht.dao.ConfigPropertiesDao)
ab_factory -> ab_factory: getObjectFromFactoryBean(factory, beanName, !synthetic)ab_factory -> ab_factory: getObject()ab_factory -> mf_bean
mf_bean -> mf_bean: getObject()\n<color red>getSqlSession().getMapper(this.mapperInterface)
mf_bean -> ss_template: getMapper(this.mapperInterface)ss_template -> ss_template: getMapper(Class<T> type)\n//getConfiguration().getMapper(type, <color red>this</color>)
ss_template -> config: getMapper(Class<T> type, SqlSession sqlSession)config -> mregistry: getMapper(Class<T> type, SqlSession sqlSession)
mregistry -> mp_factory:(MapperProxyFactory<T>) knownMappers.get(type)mp_factory -> mproxy: newInstance(sqlSession):\n<color red>new MapperProxy<T>(sqlSession, mapperInterface, methodCache)
mproxy --> ab_factory@enduml

activity_mybatis_conn_interceptor.puml

@startuml
skinparam style strictuml
skinparam sequenceMessageAlign direction
skinparam roundcorner 20
skinparam sequenceParticipant underline
autoactivate ontitle 【连接池使用及数据库厂商加载】活动图
participant mproxy << (C,#ADD1B2) MapperProxy >>
participant mmethod << (C,#ADD1B2) MapperMethod >>participant ss_template << (C,#ADD1B2) SqlSessionTemplate >>
note over of ss_template #aqua模板方法,控制流程
end noteparticipant dss_factory << (C,#ADD1B2) DefaultSqlSessionFactory >>
note over of dss_factory #aqua核心组件的创建
end noteparticipant sm_transaction << (C,#ADD1B2) SpringManagedTransaction >>
participant config << (C,#ADD1B2) Configuration >>participant mstatement << (C,#ADD1B2) MappedStatement >>participant ds_session << (C,#ADD1B2) DefaultSqlSession >>
participant bexecutor << (C,#ADD1B2) BaseExecutor >>
participant shandler << (C,#ADD1B2) StatementHandler >>participant interceptor_chain << (C,#ADD1B2) InterceptorChain >>
participant interceptor << (C,#ADD1B2) Interceptor >>
participant plugin << (C,#ADD1B2) Plugin >>
note over interceptor_chain, plugin拦截器
end noteparticipant dr_datasource << (C,#ADD1B2) DynamicRoutingDataSource >>
participant dbc2_datasource << (C,#ADD1B2) BasicDataSource >>
note over dr_datasource, dbc2_datasource数据源
end note-> mproxy: baseMapper.queryByList(map)
==入口==mproxy -> mproxy: Object invoke(Object proxy, \nMethod method, Object[] args)
mproxy -> mmethod: mapperMethod.execute()group #EEE 根据 SqlCommandType 执行SQLmmethod -> mmethod: Object execute(SqlSession sqlSession, Object[] args)\n <color red>// 难点:sqlSession.selectList(),\n <color red>此处的sqlSession是SqlSessionTemplate\n <color red>// 调用 this.sqlSessionProxy.<E> selectList(statement, parameter)\n <color red>  此处的 sqlSessionProxy的 invokeHandler 是SqlSessionInterceptormmethod -> ss_template: invoke(因为是代理对象,所以要执行 SqlSessionInterceptor.invoke)
endss_template -> ss_template: invoke(Object proxy, Method method, Object[] args)
ss_template -> dss_factory: getSqlSession()==对象的创建==
group  创建 SqlSessiondss_factory -> dss_factory: openSession(executorType)group #EEE 创建组件 tx、Executor、DefaultSqlSessiondss_factory -> dss_factory: openSessionFromDataSource(ExecutorType execType,\nTransactionIsolationLevel level,\nboolean autoCommit)dss_factory -> sm_transaction: <color red>创建tx\ntransactionFactory.newTransactionsm_transaction --> dss_factorydss_factory -> config: <color red>创建Executor\nconfiguration.newExecutor(tx, execType)group #EEE 创建 Executorconfig -> config: 根据 executorTyp\n初始化Batch/Reuse/Simple/CachingExecutorconfig -> interceptor_chain: (Executor) pluginAll(executor)\n<color red>包括PageInterceptor、MetaObjectInterceptor等interceptor_chain -> interceptor: interceptor.plugin(target)interceptor -> interceptor: plugin(Object executor)interceptor -> plugin: <color red>Plugin.wrap(target, this): 代理对象,\n<color red>执行的handler是各类interceptorplugin --> configendconfig --> dss_factorydss_factory -> ds_session: <color red>创建SqlSession\nnew DefaultSqlSession(configuration, executor, autoCommit)endendds_session --> ss_template==方法的执行==
ss_template -> ds_session: method.invoke(sqlSession, args)group 具体method的执行ds_session -> ds_session: selectList(String statement, Object parameter)ds_session -> config: configuration.getMappedStatement(statement)config --> ds_sessionds_session -> mstatement: sqlSource.getBoundSql()\n<color red>获取到databaseId的sqlmstatement --> ds_sessionds_session -> bexecutor: executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)group 拦截器执行bexecutor -> plugin: invoke()plugin -> interceptor: intercept()interceptor --> pluginplugin --> bexecutorendbexecutor -> bexecutor: query()bexecutor -> bexecutor: queryFromDatabase()bexecutor -> bexecutor: prepareStatement()group #EEE 从连接池中获取连接bexecutor -> bexecutor: getConnection()bexecutor -> sm_transaction: transaction.getConnection()sm_transaction -> sm_transaction: openConnectionsm_transaction -> dr_datasource: dataSource.getConnection()dr_datasource -> dr_datasource: getConnection()dr_datasource -> dbc2_datasource: dataSourceMap.get(primary)dbc2_datasource --> sm_transactionsm_transaction --> bexecutorendbexecutor -> shandler: handler.query(stmt, resultHandler)shandler --> bexecutorbexecutor --> ds_sessionendds_session --> mmethod==事务的提交==
ss_template -> ds_session: sqlSession.commit(true)
ds_session --> ss_template==session的关闭==
ss_template -> ds_session: closeSqlSession()
ds_session --> ss_template@enduml

class_baomidou.puml

@startuml
skinparam linetype orthotitle 【动态数据源】类图' java
namespace javax.sql #EEE {interface Wrapper {unwrap(java.lang.Class<T> iface): <T> TisWrapperFor(java.lang.Class<?> iface): boolean}interface CommonDataSource {+ getLogWriter(): PrintWriter+ setLogWriter(PrintWriter out): void+ setLoginTimeout(int seconds): void+ getLoginTimeout(): int+ getParentLogger(): Logger}interface DataSource {--+ getConnection(): Connection+ getConnection(String username, String password): Connection}Wrapper <|-- DataSourceCommonDataSource <|-- DataSource
}' spring
namespace org.springframework.beans.factory #EEE {interface InitializingBean {+ {static} afterPropertiesSet(): void}
}namespace org.springframework.jdbc.dataSource #EEE {abstract class AbstractDataSource {}note right: 实现了CommonDataSource的基本操作javax.sql.DataSource <|.. AbstractDataSource
}'baomidou
namespace com.baomidou.dynamic.datasource.ds #EEE {abstract class AbstractRoutingDataSource  {# {abstract} determineDataSource(): DataSource# {abstract} getPrimary(): String+ getConnection(): Connection}org.springframework.jdbc.dataSource.AbstractDataSource <|--- AbstractRoutingDataSource
}namespace com.baomidou.dynamic.datasource #EEE {abstract class DynamicRoutingDataSource   {--+ @Override determineDataSource(): DataSource+ @Override getPrimary(): String}com.baomidou.dynamic.datasource.ds.AbstractRoutingDataSource <|-- DynamicRoutingDataSourceorg.springframework.beans.factory.InitializingBean <|.. DynamicRoutingDataSource
}namespace com.baomidou.dynamic.datasource.provider #EEE {interface DynamicDataSourceProvider {+ {static} loadDataSources(): Map<String, DataSource>}abstract class AbstractDataSourceProvider {- defaultDataSourceCreator: DefaultDataSourceCreator- dynamicDataSourceProperties: DynamicDataSourceProperties--# createDataSourceMap(Map<String, DataSourceProperty> dataSourcePropertiesMap): Map<String, DataSource>}class YmlDynamicDataSourceProvider  {- dataSourcePropertiesMap: Map<String, DataSourceProperty>--+ @Override loadDataSources(): Map<String, DataSource>}DynamicDataSourceProvider <|.. AbstractDataSourceProviderAbstractDataSourceProvider <|-- YmlDynamicDataSourceProvider
}namespace com.baomidou.dynamic.datasource.creator #EEE {interface DataSourceCreator {+ {static} createDataSource(DataSourceProperty dataSourceProperty): DataSource+ {static} support(DataSourceProperty dataSourceProperty): boolean}abstract class AbstractDataSourceCreator {# properties: DynamicDataSourceProperties# dataSourceInitEvent: DataSourceInitEvent+ {abstract} doCreateDataSource(DataSourceProperty dataSourceProperty): DataSource--+ createDataSource(DataSourceProperty dataSourceProperty): DataSource}class DefaultDataSourceCreator  {- creators: List<DataSourceCreator>--+ createDataSource(DataSourceProperty dataSourceProperty): DataSource}class BasicDataSourceCreator  {- creators: List<DataSourceCreator>--static {}+ @Override doCreateDataSource(DataSourceProperty dataSourceProperty): DataSource+ @Override support(DataSourceProperty dataSourceProperty): boolean}note left of BasicDataSourceCreator::staticbuilderClass = Class.forName("org.springframework.boot.jdbc.DataSourceBuilder");builderClass = Class.forName("org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder");end noteDataSourceCreator <|.. AbstractDataSourceCreatorAbstractDataSourceCreator <|-- BasicDataSourceCreator
}namespace com.baomidou.dynamic.datasource.spring.boot.autoconfigure #EEE {class DataSourceProperty {- type: Class<? extends DataSource>- driverClassName: String- url: String- username: String- password: String- druid: DruidConfig- dbcp2: Dbcp2Config...}note left of DataSourceProperty::druid@NestedConfigurationPropertyend noteclass DynamicDataSourceProperties {+ {static} PREFIX: String- primary: String- strict: Boolean- datasource: Map<String, DataSourceProperty>- druid: DruidConfig- dbcp2: Dbcp2Config...}note left of DynamicDataSourceProperties::PREFIXvalue = springframework.datasource.dynamicend noteclass DynamicDataSourceCreatorAutoConfiguration {--+ dbcp2DataSourceCreator(): Dbcp2DataSourceCreator+ basicDataSourceCreator(): BasicDataSourceCreator+ dataSourceCreator(List<DataSourceCreator> dataSourceCreators): DefaultDataSourceCreator}class DynamicDataSourceAutoConfiguration  {- properties: DynamicDataSourceProperties- dataSourcePropertiesCustomizers: List<DynamicDataSourcePropertiesCustomizer>--+ ymlDynamicDataSourceProvider(): DynamicDataSourceProvider+ dataSource(): DataSource+ @Override afterPropertiesSet(): void}com.baomidou.dynamic.datasource.DynamicRoutingDataSource --[hidden] DynamicDataSourcePropertiesDynamicDataSourceProperties - DataSourcePropertyDynamicDataSourceProperties -- DynamicDataSourceAutoConfigurationDynamicDataSourceAutoConfiguration - DynamicDataSourceCreatorAutoConfiguration: @Import
}@enduml

class_mybatis.puml

@startuml
skinparam linetype orthotitle 【MyBatis】类图' java
namespace javax.lang #EEE {interface AutoCloseable {--+ close(): void}
}namespace javax.io #EEE {interface Closeable {--+ close(): void}javax.lang.AutoCloseable <|-- Closeable
}namespace javax.lang.reflect #EEE {interface InvocationHandler {--+ invoke(Object proxy, Method method, Object[] args): Object}
}namespace javax.sql #EEE {interface Connection {--+ prepareStatement(String sql): PreparedStatement+ setSavepoint(): Savepoint+ setAutoCommit(boolean autoCommit): void+ rollback(Savepoint savepoint): void+ getTransactionIsolation(): int+ setTransactionIsolation(): void+ rollback(): void+ commit(): void+ @Override close(): void}javax.lang.AutoCloseable <|-- Connection
}'spring
namespace org.springframework.context #EEE {interface ApplicationListener {--+ onApplicationEvent(E event): void}
}namespace org.springframework.core.io #EEE {interface Resource {--+ exists(): boolean+ isReadable(): boolean+ isOpen(): boolean+ getURL(): URL+ getFile(): File}
}namespace org.springframework.beans.factory #EEE {interface BeanFactory {--+ getBean(Class<T> requiredType): T+ isSingleton(String name): boolean+ isPrototype(String name): boolean}interface FactoryBean<T> {--+ getObject(): T}interface InitializingBean {+ {static} afterPropertiesSet(): void}
}'ibatis
namespace org.apache.ibatis #EEE {interface SqlSession {--+ getMapper(Class<T> type): T+ getConfiguration(): Configuration+ getConnection(): Connection+ selectOne(String statement): T+ selectList(String statement): List<E>+ select(String statement, ResultHandler handler): void+ insert(String statement): int+ update(String statement): int+ delete(String statement): int+ commit(): void+ rollback(): void+ @Override close(): void}interface SqlSessionFactory {--+ openSession(): SqlSession+ getConfiguration(): Configuration}class Configuration {# environment: Environment# objectFactory: DefaultObjectFactory# mappedStatements: Map<String, MappedStatement>}class SqlSessionTemplate {- sqlSessionFactory: SqlSessionFactory- executorType: ExecutorType- sqlSessionProxy: SqlSession, 代理的是 SqlSessionInterceptor}javax.io.Closeable <|-- SqlSessionjavax.sql.Connection --[hidden] SqlSessionSqlSession <.. SqlSessionFactory: openSession()SqlSessionFactory ..> Configuration: getConfiguration()SqlSessionTemplate .|> SqlSession}namespace org.apache.ibatis.binding #EEE {class MapperProxyFactory {- mapperInterface: Class<T>- methodCache: Map<Method, MapperMethod>--+ newInstance(SqlSession sqlSession): T# newInstance(MapperProxy<T> mapperProxy): T}class MapperProxy {--+ invoke(Object proxy, Method method, Object[] args): Object}class MapperMethod {--+ execute(SqlSession sqlSession, Object[] args): Object}MapperProxyFactory ..> MapperProxy: new MapperProxy()MapperProxy ..> MapperMethod: mapperMethod.execute()
}namespace org.apache.ibatis.executor #EEE {interface Executor {--+ update(MappedStatement ms, Object parameter): int+ query(MappedStatement ms, \nObject parameter, \nRowBounds rowBounds, \nResultHandler resultHandler, \nCacheKey cacheKey, \nBoundSql boundSql): List<E>+ commit(boolean required): void+ rollback(boolean required): void+ getTransaction(): Transaction+ close(boolean forceRollback): void}org.apache.ibatis.binding.MapperMethod --> Executor}namespace org.apache.ibatis.plugin #EEE {interface Interceptor {--+ intercept(Invocation invocation): Object+ plugin(Object target): Object+ setProperties(Properties properties): void}class Plugin {- target: Object- interceptor: Interceptor- signatureMap: Map<Class<?>, Set<Method>>--+ @Override invoke(Object proxy, Method method, Object[] args): Object}javax.lang.reflect.InvocationHandler <|-- PluginPlugin <. Interceptor: Plugin.wrap
}namespace org.apache.ibatis.transaction #EEE {interface Transaction {--+ getConnection(): Connection+ commit(): void+ rollback(): void+ close(): void}interface TransactionFactory {--+ newTransaction(Connection conn): Transaction+ newTransaction(DataSource dataSource, \n TransactionIsolationLevel level, boolean autoCommit): Transaction}javax.sql.Connection <.. Transaction: getConnection()Transaction <.. TransactionFactory: newTransaction()
}namespace org.apache.ibatis.mapping #EEE {class Environment {- id: String- transactionFactory: TransactionFactory- dataSource: DataSource}interface DatabaseIdProvider {--setProperties(Properties p): voidgetDatabaseId(DataSource dataSource): String}class VendorDatabaseIdProvider {--+ @Override setProperties(Properties p): void+ @Override getDatabaseId(DataSource dataSource): String- getDatabaseName(DataSource dataSource): String}DatabaseIdProvider <|.. VendorDatabaseIdProvider
}'mybatis
namespace org.mybatis #EEE {class SqlSessionFactoryBean {- configLocation: Resource- configuration: Configuration- mapperLocations: Resource[]- dataSource: DataSource- transactionFactory: TransactionFactory- sqlSessionFactoryBuilder: SqlSessionFactoryBuilder- sqlSessionFactory: SqlSessionFactory- plugins: Interceptor[]- databaseIdProvider: DatabaseIdProvider--+ @Override afterPropertiesSet(): void+ @Override getObject(): SqlSessionFactory+ @Override onApplicationEvent(ApplicationEvent event): void}note left of SqlSessionFactoryBean入口核心this.sqlSessionFactory = buildSqlSessionFactory()end noteorg.springframework.beans.factory.FactoryBean <|.. SqlSessionFactoryBeanorg.springframework.beans.factory.InitializingBean <|.. SqlSessionFactoryBeanorg.springframework.context.ApplicationListener <|.. SqlSessionFactoryBean
}@enduml

写在后面

  如果本文内容对您有价值或者有启发的话,欢迎点赞、关注、评论和转发。您的反馈和陪伴将促进我们共同进步和成长。


系列文章

【UML】- 类图
【GitHub】- design-pattern(设计模式)
【连接池】-从源码到适配(上),你遇到过数据库连接池的问题吗?This connection has been closed
【连接池】-从源码到适配(下)使用dynamic-datasource导致连接池没生效(升级版本)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/228802.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

thinkphp6.0升级到8.0

目录 一&#xff1a;升级过程 二&#xff1a;报错处理 最近写的项目需要使用thinkphp8.0&#xff0c;之前的老项目需要从php6.0升级到8.0&#xff0c;特此记录下升级过程。 一&#xff1a;升级过程 查看版本&#xff1a; php think version,我目前的版本是6.1.4 生成thin…

WPF+Halcon 培训项目实战(8-9):WPF+Halcon初次开发

文章目录 前言相关链接项目专栏运行环境匹配图片WPF Halcon组件HSmartWindowControlWPF绑定读取图片运行代码运行结果 抖动问题解决运行结果 绘制矩形绘制图像会消失 绘制对象绑定事件拖动事件 前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想…

C/C++面向对象(OOP)编程-回调函数详解(回调函数、C/C++异步回调、函数指针)

本文主要介绍回调函数的使用&#xff0c;包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;C/C精进之路 &…

Spark魔力:招聘网站数据深度分析系统

Spark魔力&#xff1a;招聘网站数据深度分析系统 简介数据集技术栈功能特点创新点 简介 在本文中&#xff0c;我们将介绍一款基于Spark的招聘网站数据分析系统&#xff0c;该系统使用爬取的前程无忧招聘数据。通过结合Flask、Pandas、PySpark、以及MySQL等技术&#xff0c;实现…

【汇编笔记】初识汇编-内存读写

汇编语言的由来&#xff1a; CPU是计算机的核心&#xff0c;由于计算机只认识二进制&#xff0c;所以CPU执行的指令是二进制。 我们要想让CPU工作&#xff0c;就得给他提供它认识的指令&#xff0c;这一系列的指令的集合&#xff0c;称之为指令集。 指令集&#xff1a; 不同的体…

单片机键盘程序设计举例

1、键盘与的连接 图3键盘连接 图4单片机与键盘接口图 2、通过1/0口连接。将每个按钮的一端接到单片机的I/O口&#xff0c;另一端接地&#xff0c;这是最简单的办法&#xff0c;如图3所示是实验板上按钮的接法&#xff0c;四个按钮分别接到P3.2 、P3.3、P3.4和P3.5。对于这种键…

css 设置鼠标覆盖显示菜单

鼠标覆盖到“全部分类”效果如下 鼠标放到“精品推荐”效果如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…

【CF比赛记录】—— Good Bye 2023(A、B、C)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;CF比赛记录 &#x1f48c;其他专栏&#xff1a; &#x1f534;每日一题 &#x1f7e1; cf闯关练习 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓…

mysql基础-表操作

环境&#xff1a; 管理工具&#xff1a;Navicat 数据库版本&#xff1a;5.7.37 mysql的版本&#xff0c;我们可以通过函数&#xff0c;version()进行查看&#xff0c;本次使用的版本如下&#xff1a; 目录 1.管理工具 1.1创建表 1.2.修改表名 1.3.复制表 1.4.删除表 2…

C#上位机与欧姆龙PLC的通信08----开发自己的通讯库读写数据

1、介绍 前面已经完成了7项工作&#xff1a; C#上位机与欧姆龙PLC的通信01----项目背景-CSDN博客 C#上位机与欧姆龙PLC的通信02----搭建仿真环境-CSDN博客 C#上位机与欧姆龙PLC的通信03----创建项目工程-CSDN博客 C#上位机与欧姆龙PLC的通信04---- 欧姆龙plc的存储区 C#上…

Linux常用命令大全总结及讲解(超详细版)

前言&#xff1a; Linux 是一个基于Linux 内核的开源类Unix 操作系统&#xff0c;Linus Torvalds于 1991 年 9 月 17 日首次发布的操作系统内核。Linux 通常打包为Linux 发行版。 Linux 最初是为基于Intel x86架构的个人计算机开发的&#xff0c;但此后被移植到的平台比任何其…

前端实现websocket类封装

随着Web应用程序的发展&#xff0c;越来越多的人开始利用Websocket技术来构建实时应用程序。Websocket是一种在客户端和服务器之间建立持久连接的协议。这种协议可以在一个单独的连接上实现双向通信。与HTTP请求-响应模型不同&#xff0c;Websocket允许服务器自主地向客户端发送…

OpenCV-Python(21):OpenCV中的轮廓性质

3.轮廓的性质 本文我们将主要学习基于轮廓来提取一些经常使用的对象特征。 3.1 长宽比 边界矩形的宽高比&#xff1a; x,y,w,h cv2.boundingRect(cnt) aspect_ratio float(w)/h 3.2 Extent 轮廓面积与边界矩形面积的比。 area cv2.contourArea(cnt) x,y,w,h cv2.bounding…

基于JavaWeb实验室预约管理系统(源码+数据库+文档)

一、项目简介 本项目是一套基于JavaWeb实验室预约管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;e…

simulink代码生成(五)——ePWM模块初级应用

前面分别讲到了SCI及ADC的配置及使用&#xff0c;现在梳理一下ePWM的配置和使用&#xff1b; 先打一些基础的DSP28335的基础知识&#xff1b; F28335 关于ePWM中断与SOC采样信号的一些思考_socasel-CSDN博客 F28335 ePWM模块简介——TMS320F28335学习笔记&#xff08;四&…

MySQL数据库索引优化

一、引言 1. 索引的重要性 MySQL数据库索引的重要性主要体现在&#xff0c;一是查询速度优化&#xff0c;索引可以极大地提高查询速度。对于没有索引的表&#xff0c;MySQL必须进行全部扫描来找到所需的行&#xff0c;如果表中数据量很大&#xff0c;那么通常很慢。通过适当的…

iPhone 13 Pro 更换『移植电芯』和『超容电池』体验

文章目录 考虑换电池Ⅰ 方案一Ⅱ 方案二 总结危险 Note系列地址 简 述: 首发买的iPhone 13P &#xff08;2021.09&#xff09;&#xff0c;随性使用一年出头&#xff0c;容量就暴跌 85%&#xff0c;对比朋友一起买的同款&#xff0c;还是95%。这已经基本得一天两充 >_<&a…

数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)

目录 1、概述 2、环境准备 3、读写分离实验 3.1、安装jdk 3.2、安装Mycat 3.3、配置Mycat 3.3.1、配置schema.xml ​​​​3.3.2、配置server.xml 3.4、修改主从机远程登陆权限 3.4.1、主机 3.4.2、从机 3.5、启动Mycat 3.6、登录Mycat 3.7、验证 1、概述 读写分…

模型量化之AWQ和GPTQ

什么是模型量化 模型量化&#xff08;Model Quantization&#xff09;是一种通过减少模型参数表示的位数来降低模型计算和存储开销的技术。一般来说&#xff0c;模型参数在深度学习模型中以浮点数&#xff08;例如32位浮点数&#xff09;的形式存储&#xff0c;而模型量化可以…

OpenCV-Python(21):轮廓特征及周长、面积凸包检测和形状近似

2. 轮廓特征 轮廓特征是指由轮廓形状和结构衍生出来的一些特征参数。这些特征参数可以用于图像识别、目标检测和形状分析等应用中。常见的轮廓特征包括&#xff1a; 面积&#xff1a;轮廓所包围的区域的面积。周长&#xff1a;轮廓的周长&#xff0c;即轮廓线的长度。弧长&…