【源码】-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.mzph.cn/news/585528.shtml

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

相关文章

光电耦合器:什么是隔离放大器

隔离放大器是现代电子系统中的关键组件&#xff0c;在维持信号完整性和确保敏感设备的安全方面发挥着关键作用。隔离放大器采用的关键技术之一是光耦合器&#xff0c;这是一种设计用于传输信号同时电隔离输入和输出电路的器件。在本文中&#xff0c;我们深入研究隔离放大器领域…

基于Java网上点餐系统设计与实现

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

提供电商API-100+接口,等你来试用(免费测试)

什么是 API 调用&#xff1f; 应用程序编程接口 (API)是一个程序与另一个程序交互的一种方式。API 调用是它们交互的媒介。API 调用&#xff08;或 API 请求&#xff09;是发送到服务器的消息&#xff0c;要求 API 提供服务或信息。 如果 Jan 招待很多客人共进晚餐&#xff0c…

Linux磁盘阵列

一.RAID磁盘阵列介绍 RAID&#xff08;Redundatnt Array of lndependent Disks&#xff09;&#xff0c;全称为&#xff1a;独立冗余磁盘阵列 解释&#xff1a; RAID是一种把多块独立的硬盘&#xff08;物理硬盘&#xff09;按不同的方式组合起来形成一个硬盘组&#xff08;逻…

netstat命令使用

在线安装 yum install -y net-tools 离线安装 下载本文关联的资源 解压得到离线安装包 拷贝到服务器 执行离线安装命令&#xff0c;需要在rpm文件所在路径执行 # 离线安装 rpm -Uvh --force --nodeps *.rpm 使用 netstat -nltp

【华为机试】2023年真题B卷(python)-解密犯罪时间

一、题目 题目描述&#xff1a; 警察在侦破一个案件时&#xff0c;得到了线人给出的可能犯罪时间&#xff0c;形如 “HH:MM” 表示的时刻。 根据警察和线人的约定&#xff0c;为了隐蔽&#xff0c;该时间是修改过的&#xff0c;解密规则为&#xff1a; 利用当前出现过的数字&am…

Python武器库开发-武器库篇之上传本地仓库到Git(三十八)

武器库篇之上传本地仓库到Git(三十八) 当我们在Git中创建远程仓库和进行了SSH key免密登陆之后&#xff0c;我们点击 Your respositories 可以查看我们所创建的远程仓库&#xff0c;如图所示&#xff1a; 如果我们需要将本地的仓库上传到Git&#xff0c;首先我们需要建立一个本…

大数据学习(30)-Spark Shuffle

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…

高斯泼溅的全面概述

一、说明 高斯泼溅是一种用于表示 3D 场景和渲染新颖视图的方法&#xff0c;在“实时辐射场渲染的 3D 高斯泼溅”中引入。它可以被认为是 NeRF 类模型的替代品&#xff0c;就像当年的 NeRF 一样&#xff0c;高斯分布导致了许多新的研究工作&#xff0c;这些工作选择将其用作各种…

2020年认证杯SPSSPRO杯数学建模B题(第一阶段)分布式无线广播全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 B题 分布式无线广播 原题再现&#xff1a; 以广播的方式来进行无线网通信&#xff0c;必须解决发送互相冲突的问题。无线网的许多基础通信协议都使用了令牌的方法来解决这个问题&#xff0c;在同一个时间段内&#xff0c;只有唯一一个拿到令牌…

世界经济论坛制定了五项指导原则,实现跨OT环境的网络安全。

内容概述&#xff1a; 世界经济论坛在其题为“解锁工业环境中的网络弹性&#xff1a;五项原则”的报告中列出&#xff1a;原则一&#xff1a;执行全面风险管理OT 环境、原则二&#xff1a;确保OT工程师和安装操作员对OT网络安全负责、原则三&#xff1a;与高层组织领导、战略规…

冒泡排序--------(C每日一题)

冒泡排序&#xff1a; 每次将相邻的两个数比较,将小的调到前头--升序 冒泡排序一个结论&#xff1a; n个数要进行n-1轮比较&#xff0c;第j轮要进行n-j次两两比较 循环体代码&#xff1a; int main() {int i, j,n,a[10],t;//n是几个数比较for(j1;j<n-1;j)//控制轮次for…

一篇文章深入认识微服务SpringCloud和Dubbo的区别

1、SpringCloud是什么 SpringCloud, 基于SpringBoot提供了一套微服务解决方案&#xff0c;包括服务注册与发现&#xff0c;配置中心&#xff0c;全链路监控&#xff0c;服务网关&#xff0c;负载均衡&#xff0c;熔断器等组件&#xff0c;除了基于NetFlix的开源组件做高度抽象…

鸿蒙系列--组件介绍之容器组件

一、Badge 描述&#xff1a;给其他组件添加标记 子组件&#xff1a;支持单个子组件 1.创建数字标记 Badge(value: {count: number, position?: BadgePosition, maxCount?: number, style: BadgeStyle}) 2.创建字符串标记 Badge(value: {value: string, position?: Badge…

【Bootstrap学习 day4】

Bootstrap5 列表组 使用Bootstrap创建列表 可以创建三种不类型的HTML列表&#xff1a; 无序列表—顺序无关紧要的项目列表。无序列表中的列表标有项目符号&#xff0c;例如。、等ul>li有序列表—顺序确实很重要的项目列表。有序列表中的列表项用数字标记&#xff0c;例如1、…

linux休眠机制介绍

一、概述 Linux系统提供了休眠和低功耗模式&#xff0c;可以帮助节省电力和延长电池寿命&#xff0c;休眠对应的另外一种模式就是唤醒。 二、常用的休眠方式 常用的休眠方式有freeze,standby, mem, disk&#xff0c;hibernate freeze: 冻结所有的进程&#xff0c;包括用户空…

【设计模式】外观模式

文章目录 前言一、外观模式1.案例2.优缺点3.使用场景4.源码解析 总结 前言 【设计模式】外观模式 一、外观模式 有些人可能炒过股票&#xff0c;但其实大部分人都不太懂&#xff0c;这种没有足够了解证券知识的情况下做股票是很容易亏钱的&#xff0c;刚开始炒股肯定都会想&am…

Can‘t locate IPC/Cmd.pm in @INC (@INC contains:解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

适应变化:动态预测在机器学习中的作用

一、介绍 机器学习 (ML) 中的动态预测是指随着新数据的出现而不断更新预测的方法。这种方法在从医疗保健到金融等各个领域越来越重要&#xff0c;其中实时数据分析和最新预测可以带来更好的决策和结果。在本文中&#xff0c;我将讨论机器学习中动态预测的概念、其优势、挑战以及…

网络瑞士军刀应用Cyber​​Chef

本文软件由网友 zxc 推荐&#xff1b; 什么是 Cyber​​Chef ? Cyber​​Chef 是一款简单、直观的网络应用程序&#xff0c;用于在网络浏览器中执行各种“网络”操作。这些操作包括 XOR 和 Base64 等简单编码、AES、DES 和 Blowfish 等更复杂的加密、创建二进制和十六进制转储…