From ace1e8f72463532368961bf7bf006fba6b455411 Mon Sep 17 00:00:00 2001 From: quanhengf Date: Wed, 11 Dec 2019 20:53:28 +0800 Subject: [PATCH] =?UTF-8?q?mybatis=E5=88=9D=E5=A7=8B=E5=8C=96=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 + .../核心处理层/1、MyBatis初始化.md | 203 ++++++++++++++++++ .../核心处理层/2、ResultSetHandler.md | 0 .../核心处理层/3、Executor组件.md | 0 .../学习心得/设计模式/设计模式.md | 5 +- 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 docs/Mybatis/核心处理层/2、ResultSetHandler.md create mode 100644 docs/Mybatis/核心处理层/3、Executor组件.md diff --git a/README.md b/README.md index 48ae099..0a5fc2b 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,10 @@ ### SpringJDBC + ### Spring事务 + ### Spring源码故事(瞎编版) - [面筋哥 IoC 容器的一天(上)](/docs/Spring/IoC/面筋哥IoC容器的一天(上).md) @@ -37,6 +39,8 @@ - [缓存模块](docs/Mybatis/基础支持层/4、缓存模块.md) ### 核心处理层 - [MyBatis初始化](docs/Mybatis/核心处理层/1、MyBatis初始化.md) +- [ResultSetHandler](docs/Mybatis/核心处理层/2、ResultSetHandler.md) +- [Executor组件](docs/Mybatis/核心处理层/3、Executor组件.md) ## Netty ### IO @@ -46,6 +50,8 @@ ## Redis +## Tomcat + ## 学习心得 ### 个人经验 - [初级开发者应该从 spring 源码中学什么](docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md) diff --git a/docs/Mybatis/核心处理层/1、MyBatis初始化.md b/docs/Mybatis/核心处理层/1、MyBatis初始化.md index e69de29..e31c3b1 100644 --- a/docs/Mybatis/核心处理层/1、MyBatis初始化.md +++ b/docs/Mybatis/核心处理层/1、MyBatis初始化.md @@ -0,0 +1,203 @@ +和spring框架的IoC容器初始化一样,mybatis也会通过定位、解析相应的配置文件完成自己的初始化。mybatis的配置文件主要有mybatis-config.xml核心配置文件及一系列映射配置文件,另外,mybatis也会根据注解进行配置。 +## 1 BaseBuilder +mybatis初始化的主要内容是加载并解析mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。mybatis的初始化入口是SqlSessionFactoryBuilder的build()方法。 +```java +public class SqlSessionFactoryBuilder { + + public SqlSessionFactory build(Reader reader) { + return build(reader, null, null); + } + + public SqlSessionFactory build(Reader reader, String environment) { + return build(reader, environment, null); + } + + public SqlSessionFactory build(Reader reader, Properties properties) { + return build(reader, null, properties); + } + + /** + * build方法的主要实现 + */ + public SqlSessionFactory build(Reader reader, String environment, Properties properties) { + try { + // SqlSessionFactory会创建XMLConfigBuilder对象来解析mybatis-config.xml配置文件 + // XMLConfigBuilder继承自BaseBuilder抽象类,顾名思义这一系的类使用了 建造者设计模式 + XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); + // 解析配置文件的内容 到Configuration对象,根据到Configuration对象 + // 创建DefaultSqlSessionFactory对象,然后返回 + return build(parser.parse()); + } catch (Exception e) { + throw ExceptionFactory.wrapException("Error building SqlSession.", e); + } finally { + ErrorContext.instance().reset(); + try { + // 关闭配置文件输入流 + reader.close(); + } catch (IOException e) { + // Intentionally ignore. Prefer previous error. + } + } + } + + public SqlSessionFactory build(Configuration config) { + return new DefaultSqlSessionFactory(config); + } +``` +BaseBuilder中的核心字段如下: +```java +public abstract class BaseBuilder { + + // 保存了mybatis的几乎所以核心配置信息,全局唯一 + protected final Configuration configuration; + // 在mybatis-config.xml中可以通过标签定义别名 + protected final TypeAliasRegistry typeAliasRegistry; + // 在mybatis-config.xml中可以通过标签添加自定义TypeHandler + // TypeHandler用于完成JDBC数据类型与Java类型的相互转换,所有的TypeHandler + // 都保存在typeHandlerRegistry中 + protected final TypeHandlerRegistry typeHandlerRegistry; + + public BaseBuilder(Configuration configuration) { + this.configuration = configuration; + this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); + this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); + } +} +``` +BaseBuilder中的typeAliasRegistry和typeHandlerRegistry字段均来自于configuration,通过BaseBuilder的构造方法可以看到详细内容。 +## 2 XMLConfigBuilder +XMLConfigBuilder是BaseBuilder的众多子类之一,主要负责解析mybatis-config.xml配置文件。它通过调用parseConfiguration()方法实现整个解析过程,其中,mybatis-config.xml配置文件中的每个节点都被封装成了一个个相应的解析方法,parseConfiguration()方法只是依次调用了这些解析方法而已。 +```java +public class XMLConfigBuilder extends BaseBuilder { + + // 标记是否解析过mybatis-config.xml文件 + private boolean parsed; + // 用于解析mybatis-config.xml的解析器 + private final XPathParser parser; + // 标识配置的名称,默认读取标签的default属性 + private String environment; + // 创建并缓存Reflector对象 + private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); + + /** + * 解析的入口,调用了parseConfiguration()进行后续的解析 + */ + public Configuration parse() { + // parsed标志位的处理 + if (parsed) { + throw new BuilderException("Each XMLConfigBuilder can only be used once."); + } + parsed = true; + // 在mybatis-config.xml配置文件中查找节点,并开始解析 + parseConfiguration(parser.evalNode("/configuration")); + return configuration; + } + + private void parseConfiguration(XNode root) { + try { + // 根据root.evalNode("properties")中的值就可以知道具体是解析哪个标签的方法咯 + propertiesElement(root.evalNode("properties")); + Properties settings = settingsAsProperties(root.evalNode("settings")); + loadCustomVfs(settings); + typeAliasesElement(root.evalNode("typeAliases")); + pluginElement(root.evalNode("plugins")); + objectFactoryElement(root.evalNode("objectFactory")); + objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); + reflectorFactoryElement(root.evalNode("reflectorFactory")); + settingsElement(settings); + // read it after objectFactory and objectWrapperFactory issue #631 + environmentsElement(root.evalNode("environments")); + databaseIdProviderElement(root.evalNode("databaseIdProvider")); + typeHandlerElement(root.evalNode("typeHandlers")); + mapperElement(root.evalNode("mappers")); + } catch (Exception e) { + throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); + } + } +``` +mybatis中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。 +### 2.1 解析<typeHandlers>标签 +```java + private void typeHandlerElement(XNode parent) throws Exception { + if (parent != null) { + // 处理下的所有子标签 + for (XNode child : parent.getChildren()) { + // 处理标签 + if ("package".equals(child.getName())) { + // 获取指定的包名 + String typeHandlerPackage = child.getStringAttribute("name"); + // 通过typeHandlerRegistry的register(packageName)方法 + // 扫描指定包中的所有TypeHandler类,并进行注册 + typeHandlerRegistry.register(typeHandlerPackage); + } else { + // Java数据类型 + String javaTypeName = child.getStringAttribute("javaType"); + // JDBC数据类型 + String jdbcTypeName = child.getStringAttribute("jdbcType"); + String handlerTypeName = child.getStringAttribute("handler"); + Class javaTypeClass = resolveClass(javaTypeName); + JdbcType jdbcType = resolveJdbcType(jdbcTypeName); + Class typeHandlerClass = resolveClass(handlerTypeName); + // 注册 + if (javaTypeClass != null) { + if (jdbcType == null) { + typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); + } else { + typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); + } + } else { + typeHandlerRegistry.register(typeHandlerClass); + } + } + } + } + } +``` +### 2.2 解析<environments>标签 +```java + /** + * mybatis可以配置多个环境,分别用于开发、测试及生产等, + * 但每个SqlSessionFactory实例只能选择其一 + */ + private void environmentsElement(XNode context) throws Exception { + if (context != null) { + // 如果未指定XMLConfigBuilder的environment字段,则使用default属性指定的环境 + if (environment == null) { + environment = context.getStringAttribute("default"); + } + // 遍历节点 + for (XNode child : context.getChildren()) { + String id = child.getStringAttribute("id"); + if (isSpecifiedEnvironment(id)) { + // 实例化TransactionFactory + TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); + // 创建DataSourceFactory和DataSource + DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); + DataSource dataSource = dsFactory.getDataSource(); + // 创建的Environment对象中封装了上面的TransactionFactory对象和DataSource对象 + Environment.Builder environmentBuilder = new Environment.Builder(id) + .transactionFactory(txFactory) + .dataSource(dataSource); + // 为configuration注入environment属性值 + configuration.setEnvironment(environmentBuilder.build()); + } + } + } + } +``` +### 2.3 解析<databaseIdProvider>标签 +```java + +``` +### 2.4 解析<mappers>标签 +```java + +``` + + + + + + + + diff --git a/docs/Mybatis/核心处理层/2、ResultSetHandler.md b/docs/Mybatis/核心处理层/2、ResultSetHandler.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/Mybatis/核心处理层/3、Executor组件.md b/docs/Mybatis/核心处理层/3、Executor组件.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/学习心得/设计模式/设计模式.md b/docs/学习心得/设计模式/设计模式.md index af5ceb6..46c08ac 100644 --- a/docs/学习心得/设计模式/设计模式.md +++ b/docs/学习心得/设计模式/设计模式.md @@ -1,10 +1,13 @@ ## 六大原则 - 1. 单一职责:一个类只负责唯一一项职责 2. 依赖倒置:即面向接口编程,系统的高层模块(顶层接口、顶层抽象类等)不应该依赖底层模块(具体实现类),当需求发生变化时,对外接口不变,只要提供新的实现类即可。 3. 接口隔离:尽量设计出功能单一的接口,避免实现类实现很多不必要的接口方法 4. **开放-封闭:对扩展开放,对修改关闭**,本原则是设计模式的终极目标 5. 迪米特法则:尽量减少类之间的耦合性 6. 里氏替换:继承体系的设计要合理 + ## 装饰器模式 + + +