第02篇:Mybatis配置文件解析

第02篇:Mybatis配置文件解析

本篇主要内容如下,由于头条页面对Markdown文档的展示问题,所以排版可能有问题。

一、配置文件分析

文件分析

在上一篇的代码中,我们看到了一个非常重要文件,这里我们先来人肉分析看,然后看下代码是如何解析的,毕竟代码也是人写的。 思路决定出路,我们如果有思路,然后在看源码会更加的具有分析的能动性。

@Test public void mapper() { // 读取配置信息(为什么路径前不用加/,因为是相对路径。maven编译后的资源文件和class文件都是在一个包下,所以不用加/就是当前包目录) InputStream mapperInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(“mybatisConfig.xml”); // 生成SqlSession工厂,SqlSession从名字上看就是,跟数据库交互的会话信息,负责将sql提交到数据库进行执行 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mapperInputStream, “development”); // 获取Mybatis配置信息 Configuration configuration = sqlSessionFactory.getConfiguration(); // 参数: autoCommit,从名字上看就是是否自动提交事务 SqlSession sqlSession = sqlSessionFactory.openSession(false); // 获取Mapper TUserMapper mapper = configuration.getMapperRegistry().getMapper(TUserMapper.class, sqlSession); TUser tUser = new TUser(); tUser.setName(“testUser1”); tUser.setTokenId(“testTokenId1”); mapper.insert(tUser); // 获取插入的数据 System.out.println(mapper.selectAll()); // 数据插入后,执行查询,然后回滚数据 sqlSession.rollback(); }

1.1 mybatisConfig.xml

tip 注意看高亮行

  • line(4) dtd文件是xml的约束文件,用于约束 xml 标签属性
  • line(8) properties标签,指定了配置信息文件是 application.properties
  • line(11-13) mybatis的配置信息
  • line(15-27) mybatis支持多环境配置
  • line(30-32) 映射文件 :::
  • 基于上面的行,我们来讲解。

    二、知识点讲解

    2.1 xml约束文件dtd

    为什么要学习dtd约束文件呢? 当你学会dtd约束文件后,你就知道这个标签有那些属性,知道标签及子标签信息。 当有一天你要写开源框架的时候,你也可以来定义你自己的配置文件规则。这部分知识了解就行。不需要死记硬背。 因为记住也基本没啥用,只要做到看到了认识,需要用了知道去哪里抄代码学习就够了。

    2.1.1 元素 & 属性 & 属性值

    dtd文件

    域 示例 语法 例子 元素 声明根元素标签 ,元素students有一个student 元素 空元素 元素 元素只出现一次 ,元素students至少有一个student 元素 元素最少出现一次 ,元素students最少有一个student 元素 声明出现零次或多次的元素 ,元素students可以有多个student,也可以一个没有 元素 声明“非…/既…”类型的内容 “ 元素 声明混合型的内容 `<!ELEMENT note (#PCDATA to 属性 属性声明 ,payment有一个属性type,类型为字符类型,默认值check

    值类型

    类型 描述 CDATA 值为字符数据 (character data) (en1 en2 ID 值为唯一的 id IDREF 值为另外一个元素的 id IDREFS 值为其他 id 的列表 NMTOKEN 值为合法的 XML 名称 NMTOKENS 值是一个实体 ENTITIES 值是一个实体列表 NOTATION 此值是符号的名称 xml: 值是一个预定义的 XML 值

    默认值参数可使用下列值

    类型 描述 值 属性的默认值 #REQUIRED 属性值是必需的 #IMPLIED 属性不是必需的 #FIXED value 属性值是固定的

    2.2 configuration标签分析

    前面我们知道了dtd约束文件,我们就可以看下,configuration标签一共有那些子标签及属性信息了。

    mybatis-3-config.dtd

    通过分析dtd文件,我们知道有那些子标签及属性信息。内容比较长。但是不是很重要。这里只要知道就行。

    后面我们看如何使用代码来解析这些标签。

    2.3 Mybatis配置解析核心逻辑

    :::tip 思路决定出路

    • line(6) sqlSessionFactory.getConfiguration()

    由此来看所有的解析都是在SqlSessionFactoryBuilder进行完成的. 具体的解析xml代码我们不研究,这里我们只要搞清楚它的调用关系,及实现的代码在哪里即可。如果这里 看懂,其实都会得到一个结论。就是mybaits的源码是比较简单的,因为他的配置是比较集中的,无论是xml方式或者是注解方式。 最终所有的配置信息都在 Configuration 类中。 :::

    @Test public void configuration() { // 读取配置信息(为什么路径前不用加/,因为是相对路径。maven编译后的资源文件和class文件都是在一个包下,所以不用加/就是当前包目录) InputStream mapperInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(“mybatisConfig.xml”); // 生成SqlSession工厂,SqlSession从名字上看就是,跟数据库交互的会话信息,负责将sql提交到数据库进行执行 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mapperInputStream, “development”); // 获取Mybatis配置信息,由此来看所有的解析都是在SqlSessionFactoryBuilder进行完成的. Configuration configuration = sqlSessionFactory.getConfiguration(); }

    2.3.1 new SqlSessionFactoryBuilder().build

    这里可以看到就是核心类就是使用 XMLConfigBuilder 进行解析。下面我们就主要分析 XMLConfigBuilder

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException(“Error building SqlSession.”, e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }

    2.3.2 核心配置类解析(XMLConfigBuilder)

    :::note 重点关注

  • line(8), 我们看到核心解析类是 XPathParser parser = new XPathParser()
  • line(17), 标签的解析都在 parseConfiguration
  • line(17), 思考下为什么先解析 propertiesElement(root.evalNode(“properties”)) :::
  • public class XMLConfigBuilder extends BaseBuilder { private boolean parsed; private final XPathParser parser; private String environment; private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); public Configuration parse() { if (parsed) { throw new BuilderException(“Each XMLConfigBuilder can only be used once.”); } parsed = true; parseConfiguration(parser.evalNode(“/configuration”)); return configuration; } private void parseConfiguration(XNode root) { try { // issue #117 read properties first propertiesElement(root.evalNode(“properties”)); Properties settings = settingsAsProperties(root.evalNode(“settings”)); loadCustomVfs(settings); loadCustomLogImpl(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); } }}

    看到上面代码是不是就恍然大悟了,原来配置文件的标签都是在这里解析呀。这里的主要思路就是将xml解析成Java对象然后放到 Configuration中。具体任何实现呢? 感兴趣可以自己研究下。

    2.3.3 Configuration属性介绍

    那么这些数据最终哪里会使用呢,我们专门留一片文章, 详细分析。这里先看看Configuration内部都有那些关键的配置类把。

    属性 解释 TypeAliasRegistry key是一个别名,value是一个class对象 Properties variables 配置文件中占位符的变量配置 InterceptorChain interceptorChain 拦截链,用于拦截方法,实现插件 ObjectFactory objectFactory 对象实例化统一的工厂方法,我们不用都反射来实例化了 ObjectWrapperFactory objectWrapperFactory 包装对象后为其提供统一的属性操作方法。我们不用通过反射来修改对象属性值了 ReflectorFactory reflectorFactory 反射工厂,用于生成一个反射信息对象,具有缓存的作用 Environment environment 环境信息包含(事务管理器和数据源) TypeHandlerRegistry typeHandlerRegistry 主要处理jdbc的返回数据,转换成Java对象 MapperRegistry mapperRegistry Mapper生成的处理类,包含代理的逻辑

    2.3.4 Mapper.xml 解析

    XMLMapperBuilder

    解析Mapper对应的xml配置文件,这里面包含了sql的信息。

    mapper的dtd约束文件更多,可以参考: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#

    这里就要介绍一个重要的类的,MapperBuilderAssistant Mapper构建辅助工具类。

    属性 解释 MapperBuilderAssistant Mapper构建辅助工具类(缓存配置) CacheRefResolver 决定如何使用缓存 ParameterMapping 当sql中使用到了#{}占位符时候,负责填充sql参数 ResultMapResolver 返回值映射 Map sqlFragments sql片段 MappedStatement Mapper方法的所有信息(出参,入参,及sql信息等)

    2.4 Mybatis可以借鉴的知识点

    2.4.1 占位符解析逻辑

    在第一篇的时候我们说过,从配置文件解析中我们能学会,如果解析占位符。并将占位符填充真实数据。这里我们就具体说下是如何解析。 还记得前面让思考下为什么先解析 propertiesElement(root.evalNode(“properties”))。

    答案就是为了先读取变量信息,方便后面给依赖的信息,给填充值。

    我们直接说答案: 具体谁来做了这个事情,从职责划分上来看,这个其实还是属于xml文件解析。所以是 XPathParser parser XPathParser中填充上变量信息,这样XPathParser在解析的时候会自动将 ${} 填充上真实的数据。

    // 执行后,会解析properties标签,并且将属性赋值给XPathParser propertiesElement(root.evalNode(“properties”)); parser.setVariables(defaults); configuration.setVariables(defaults); // XPathParser 生成节点时候,属性信息会提前处理。 public XNode(XPathParser xpathParser, Node node, Properties variables) { this.xpathParser = xpathParser; this.node = node; this.name = node.getNodeName(); this.variables = variables; this.attributes = parseAttributes(node); this.body = parseBody(node); } // 发现是占位符,就从变量中读取。 // ${datasource.driver-class-name} 替换成变量值里面的数据。 public static String parse(String string, Properties variables) { VariableTokenHandler handler = new VariableTokenHandler(variables); GenericTokenParser parser = new GenericTokenParser(“${“, “}”, handler); return parser.parse(string); }

    2.4.2 Mybatis Resources 工具

    可以从配置文件中或者网络中解析配置,生成 Resources 对象

    String resource = context.getStringAttribute(“resource”); if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } parser.setVariables(defaults); configuration.setVariables(defaults); // 从资源中获取流 InputStream inputStream = Resources.getResourceAsStream(resource) // 从url中获取流 InputStream inputStream = Resources.getUrlAsStream(url)

    2.4.3 Mybatis PropertyParser 占位符解析

    @Test public void propertyParser() { Properties variables = new Properties(); variables.put(“datasource.driver-class-name”, “com.mysql.cj.jdbc.Driver”); // 变量中有就从变量中获取 参数信息: com.mysql.cj.jdbc.Driver System.out.println(PropertyParser.parse(“参数信息: ${datasource.driver-class-name}”, variables)); // 变量中没有就直接返回key datasource.url System.out.println(PropertyParser.parse(“datasource.url”, variables)); }

    2.4.4 反射工厂 ReflectorFactory

    在Mybatis中使用到的反射地方蛮多的,那么都知道反射是相对比较耗时间,那么我们来看Mybatis是如何利用反射工厂来提高反射的性能的?

    缓存,对要使用的Class类,做反射并保存起来, 生成的对象是 Reflector。

    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

    public interface ReflectorFactory { boolean isClassCacheEnabled(); void setClassCacheEnabled(boolean classCacheEnabled); Reflector findForClass(Class type);}public class Reflector { private final Class type; private final String[] readablePropertyNames; private final String[] writablePropertyNames; private final Map setMethods = new HashMap(); private final Map getMethods = new HashMap(); private final Map setTypes = new HashMap(); private final Map getTypes = new HashMap(); private Constructor defaultConstructor; private Map caseInsensitivePropertyMap = new HashMap();} @Test public void reflector() throws Exception { ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); Reflector forClass = reflectorFactory.findForClass(TUser.class); TUser user = (TUser) forClass.getDefaultConstructor().newInstance(); forClass.getSetInvoker(“uid”).invoke(user, new Object[]{1}); forClass.getSetInvoker(“name”).invoke(user, new Object[]{“孙悟空”}); forClass.getSetInvoker(“tokenId”).invoke(user, new Object[]{“tokenId”}); // 1 System.out.println(forClass.getGetInvoker(“uid”).invoke(user, new Object[]{})); // 孙悟空 System.out.println(forClass.getGetInvoker(“name”).invoke(user, new Object[]{})); }

    2.4.5 异常上下文设计 ErrorContext

  • 在代码执行的过程中,将关键信息通过 ErrorContext.instance().message() 保存进去。利用到了线程隔离的知识。
  • ErrorContext.instance() 是利用 ThreadLocal 进行线程隔离。
  • 异常打印后,进行 reset 重置。
  • public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw wrapException(“Error updating database. Cause: ” + e, e); } finally { // 完成之后异常上下文进行重置 ErrorContext.instance().reset(); } } // 将异常上线文中报错的错误都打印出来。 public static RuntimeException wrapException(String message, Exception e) { return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e); }

    郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
    (0)
    用户投稿
    上一篇 2022年7月11日
    下一篇 2022年7月11日

    相关推荐

    • 强化App合规运营 国家网信办加固规则防线

      6月14日,国家互联网信息办公室发布新修订的《移动互联网应用程序信息服务管理规定》(以下简称新《规定》),以进一步依法监管移动互联网应用程序,促进应用程序信息服务健康有序发展。新《…

      2022年6月16日
    • QQ出现大面积盗号 疑似点了不明链接

      来源:快科技 6月27日消息,昨晚疑似出现了大面积QQ盗号现象,你的QQ号还好吗? 据多名网友反馈称,自己的QQ号列表里一下子冒出一堆群组,几乎都在发涉黄信息。 有博主判断,疑似有…

      2022年6月28日
    • 8字头股票什么意思(8字头股票什么意思呀)

      北京证券交易所股票是以4和8开头1北京证券交易所是以现有的新三板精选层为基础组建,进一步提升服务中小企业的能力,打造服务创新型中小企业主阵地北京证券交易所是因为我们国家要支持中小企…

      2022年11月11日
    • 任正非所说的寒气,为何伤不到这些行业巨头?

      任正非一句寒气逼人,道出未来3 5年的企业困境,面对巨大的不确定性和迎面扑来的寒风,走在第一梯队的大型企业,纷纷通过加速数字化布局,提高企业协同效应,增强组织抗风险能力。 然而什么…

      2022年9月8日
    • 为什么现在的软件都要求读取相册、通讯录等多种权限,安全吗?

      随着科学技术翻天覆地的发展,曾经贵不可攀的手机已经成为了人手必备的常见品。手机在我们的生活中所扮演的角色越来越重要,甚至有网友贴切的说:“一部手机在手,天下我都有”。一丝一毫也不夸…

      2022年7月26日
    • 云服务器免费(云服务器试用)

      今天小编给各位分享云服务器免费的知识,其中也会对云服务器试用进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧! 主机免费云服务器如何 三丰云是北京太极三丰云计算…

      2022年11月7日
    • 悬念揭晓,广厦大手笔3换1,辽篮卫冕拦路虎,众神归位剑指总冠军

      近日,各大CBA俱乐部疯狂在招兵买马,而对于广厦男篮来说,总决赛遭遇辽篮横扫后,决心补强锋线,随着为时多日的调整,广厦12人大名单终于悬念揭晓。分别是李金效、胡金秋、赵岩昊、孙铭徽…

      2022年7月30日
    • 电脑ip查询地址(查看自己的ip地址详细操作步骤)

      怎么查看自己的ip地址? 1、首先教大家最常用的方法,右键桌面“网上邻居”,选择属性。 2、进去之后发现电脑有几个网卡,我们要右键我们目前使用的网卡。 3、点击属性。 4、点击In…

      2022年5月6日
    • Windows效率提升神器,文件搜索应用启动——Listary

      给大家带来一款windows上十分好用的效率神器Listary,有些功能上和文件搜索神器everything类似,只是还多了其它更加好用的功能,用everything的小伙伴可以转…

      2022年6月28日
    • 龙虎榜|北方华创今日再度跌停 四机构合计净卖出2.38亿元

      【龙虎榜|北方华创今日再度跌停 四机构合计净卖出2.38亿元】财联社10月11日电,北方华创今日再度跌停,龙虎榜数据显示,深股通买入1.77亿元并卖出4.83亿元,两机构合计净买入…

      2022年10月13日

    联系我们

    联系邮箱:admin#wlmqw.com
    工作时间:周一至周五,10:30-18:30,节假日休息