前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【云+社区年度征文】借鉴了Mybatis源码解决了项目上线时的一个问题

【云+社区年度征文】借鉴了Mybatis源码解决了项目上线时的一个问题

原创
作者头像
手撕代码八百里
修改于 2020-12-21 02:03:17
修改于 2020-12-21 02:03:17
1K0
举报
文章被收录于专栏:猿计划猿计划

使用了我开发的框架,项目部署时突然出了问题,借鉴了Mybatis源码才解决

一、背景

本篇文章是我对Swagger进行了二次开发,并封装成了一个框架,发布到了maven私服,这样就可以达到拿来即用啦。但是出现了一个问题,导致打包成jar包之后某些功能无法生效,本文特针对这个问题,来阐述如何借鉴了Mybatis源码才解决的。

对swagger扩展的功能,我也对其来龙去脉,做了严谨的分析和相关扩展功能的开发,并记录下来做了梳理,并整理成了一篇文章,在技术创作101训练营第一季的时候,发布到了腾讯云+社区,参加了活动:

这篇名字:《历经14天自定义3个注解解决项目的3个Swagger难题

链接地址:https://cloud.tencent.com/developer/article/1700641

内容大纲:

1608440914350-1fe1ac74-110d-4e39-9029-2a24aafc52fd.png
1608440914350-1fe1ac74-110d-4e39-9029-2a24aafc52fd.png

这篇文章也收获了很多赞和收藏:

1608441319486-24d07b2c-9823-410e-864d-200ba9840d1a.png
1608441319486-24d07b2c-9823-410e-864d-200ba9840d1a.png

同时也被评为技术创作101训练营-第一季的前10名,拿到了“优秀创作者”的荣誉证书。

1608441764160-d737746f-e95f-485e-9fa8-8f913b746dbb.png
1608441764160-d737746f-e95f-485e-9fa8-8f913b746dbb.png

在《历经14天自定义3个注解解决项目的3个Swagger难题》中的第三部分:(实战的(二)实战二:减少在Controller中Swagger的代码,使其可以从某些文件中读取信息,自动配置Swagger的功能)。这部分在上线部署的时候突然出现了问题。随后会详细分析一下。

1608441066768-f67a25e0-d6af-49b4-8796-aaee526b6dc9.png
1608441066768-f67a25e0-d6af-49b4-8796-aaee526b6dc9.png

二、分析问题

部署服务之后,发现自己扩展的一个功能尚未生效。

就去docker上扒拉了一下日志。

先看下图,下图是我从docker的启动日志中扒拉出来的。因为我们的服务都部署在了docker中

1608447658780-cd7817ca-4969-45f6-9745-2d2f860af665.png
1608447658780-cd7817ca-4969-45f6-9745-2d2f860af665.png

看一下ReadFromFile.java这个的第298行:

是我读取文件的,初次猜测文件没有读取成功,才会造成的这个问题。

1608448093655-02ae9aa9-90e7-4156-a3db-33cead32fd14.png
1608448093655-02ae9aa9-90e7-4156-a3db-33cead32fd14.png

为了验证这个问题是不是服务器上某些配置导致的,我从cmd中使用java -jar命令运行了一下jar包:

还是不行

1608448886606-6f26a068-2ab8-4c6b-b9ee-985253a396e9.png
1608448886606-6f26a068-2ab8-4c6b-b9ee-985253a396e9.png

但是在IDEA中启动时是没有问题的。

整个结构是这样的:

1608449649230-17c94c55-7d83-4058-ac4e-06cc0dbdeeaa.png
1608449649230-17c94c55-7d83-4058-ac4e-06cc0dbdeeaa.png

说的有点多,总结一下:

出现的问题是我所开发的这个框架,以一个依赖的形式被其他项目所使用的时候,在IDEA开发环境下运行是可以的。

其中有一个功能是需要读取项目中的某些文件。但是此功能在项目被打成jar包部署在服务器的时候,却出现了问题,无法正常读取文件。

三、解决方案

遇到问题,肯定先百度一下,谷歌一下。

连续搜了好多天,也连续尝试了好多天;

1608449865266-84cf0339-c82d-41ae-a405-66b55279a3cb.png
1608449865266-84cf0339-c82d-41ae-a405-66b55279a3cb.png

别看下面我差出来了很多,但是几乎也就那几种办法。

我几乎都尝试过,但是都不是我想要的。

我想要的还很特殊:

因为我是开发的一个框架,需要从jar包中读取另一个jar包中的某些文件。

1608450190175-e615fce9-cf74-4ccf-9aac-df77da626f59.png
1608450190175-e615fce9-cf74-4ccf-9aac-df77da626f59.png

最后不得不放弃百度,就在要快放弃的时候,突然想到Mybatis和我这个是类似的,Mybatis也是一个jar包,Mybatis也是从jar包中读取xxxxMapper.xml文件进行解析的。

Mybatis的这种读取xxxMapper.xml文件的模式和我开发的框架读取md文件的设计居然一模一样。

那只好扒拉一下Mybatis的源代码进行研究一番了;以前想着去一下Mybatis的源码呢,一直没时间。现在正好,趁机学习一下Mybatis源代码。

我从搭建Mybatis的环境开始研究,

也做了比较详细的记录,如果你想学习Mybatis的话,可以持续关注我,我会把学习的过程中记录的一些东西都发布来的。

阅读链接:https://www.yuque.com/docs/share/fa80a044-49be-44ba-aa36-53cca12eee97?# 《1、构建源码环境》

1608450637716-74f36f91-9fbc-4c87-bf69-1db231e083e0.png
1608450637716-74f36f91-9fbc-4c87-bf69-1db231e083e0.png

解决方案:

  • 1、阅读Mybatis源码,找出来Mybatis如何读取的Mapper.xml文件;
  • 2、模仿Mybatis读取Mapper.xml文件的流程去改造自己的程序。

四、分析:Mybatis如何加载xxxMapper.xml文件

为了减少篇幅,本篇文章略过如何搭建Mybatis的源码环境,直接描述分析的过程;如果想看,也可以加我微信:weiyi3700,给你初稿版,或者等我整理出来Mybatis源码系列的文章,再来看也是可以的。

https://www.yuque.com/docs/share/fa80a044-49be-44ba-aa36-53cca12eee97?# 《1、构建源码环境》

(一)创建一个可以跟踪的程序

为了好跟踪Mybatis源码,使用IDEA创建了2个Model,一个是Mybatis源码项目,一个是测试程序。

1608451493409-5ae89b54-0821-41fb-9cfc-d51b8df4cc69.png
1608451493409-5ae89b54-0821-41fb-9cfc-d51b8df4cc69.png

如果搭建这套环境可以参考,我写的这篇文章,已经放在语雀上了:

https://www.yuque.com/docs/share/fa80a044-49be-44ba-aa36-53cca12eee97?# 《1、构建源码环境》

如果能访问数据,则成功:

1608452436960-a5bd1769-44a8-465f-a74a-e1704ff73ae3.png
1608452436960-a5bd1769-44a8-465f-a74a-e1704ff73ae3.png

(二)逐行分析

1、读取mybatis-config.xml文件

代码如下:

代码语言:txt
AI代码解释
复制
String resource = "mybatis-config.xml";
final Reader reader = Resources.getResourceAsReader(resource);

用鼠标点开getResourceAsReader的时候,就会看到如下图所示的:

可以看到,就是去使用流的形式去读取这个配置文件,并返回一个流对象

1608453321997-15d97c66-7805-4d2e-b547-f11f833dca0b.png
1608453321997-15d97c66-7805-4d2e-b547-f11f833dca0b.png

在源码中的执行流程如下:

1608455664761-d68535c7-3b74-4a3b-b31b-97089b622571.png
1608455664761-d68535c7-3b74-4a3b-b31b-97089b622571.png

2、创建SqlSessionFactory

SqlSessionFactory的创建的流程太复杂了,我简单总结一下步骤:

(1)从Reader的流中读取Mybatis-config.xml配置文件的数据流

(2)从流中读取 xml配置文件中的," <configuration>  .... </configuration> " 根节点。

(3)从“<configuration>  .... </configuration>”根节点中解析每个子节点的数据,例如:“mappers”、“environments”节点等;

(4)解析“mappers”节点,拿到xxxMapper.xml的存放方式和存放路径;

(5)按照“mappers”节点中配置的信息,选择性的进行读取Mapper.xml文件;

(5-1)如果是package方式的话:

(5-1-1)先判断是否为jar包,如果是就以流的形式打开;

(5-1-2)然后会把所有的资源扫描一遍,边扫描边检查是否为我们要寻找的路径,例如:com.truedei.mapper;

(5-1-3)把符合要求的url都会添加到resources中返回。

分析具体流程图

整理出了一个流程图,可以看一下:

1608461038875-c9452b26-1b12-48f3-9994-e0b6c2d9a48c.png
1608461038875-c9452b26-1b12-48f3-9994-e0b6c2d9a48c.png

到此位置,我们也就知道了,Mybatis是如何扫描到的Mapper.xml文件。

五、总结Mybatis如何扫描到的Mapper.xml文件

我们比较关心的是如何从jar包中扫描到我们想要的资源路径。

由上面的分析可见,是通过ResolverUtil类中的find()方法扫描的。

ResolverUtil.find()又调用VFS类的 VFS.getInstance().list(path)方法进行扫描

在VFS.getInstance().list(path)中比较重要的又两个方法,

一个是:getResources(path)

*(核心)一个是:list(url, path)

在list()接口中进行查找相应的path,并返回一个List<String>集合

而在list()接口中,重要的就是listResources()这个方法了:

在这个方法中,从jar文件流中查找包含path的url; 如果符合要求就添加到List中返回。

代码语言:txt
AI代码解释
复制
 protected List<String> listResources(JarInputStream jar, String path) throws IOException {
    // Include the leading and trailing slash when matching names
    if (!path.startsWith("/")) {
      path = "/" + path;
    }
    if (!path.endsWith("/")) {
      path = path + "/";
    }

    // Iterate over the entries and collect those that begin with the requested path
    List<String> resources = new ArrayList<>();
    for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
      if (!entry.isDirectory()) {
        // Add leading slash if it's missing
        StringBuilder name = new StringBuilder(entry.getName());
        if (name.charAt(0) != '/') {
          name.insert(0, '/');
        }

        // Check file name
        if (name.indexOf(path) == 0) {
          if (log.isDebugEnabled()) {
            log.debug("Found resource: " + name);
          }
          // Trim leading slash
          resources.add(name.substring(1));
        }
      }
    }
    return resources;
  }

由此可见,最中间,最核心的就是上面这段代码了。

下面name.indexOf(path)就是用来匹配是否开头包含这个路径的

代码语言:txt
AI代码解释
复制
        // Check file name
        if (name.indexOf(path) == 0) {
          if (log.isDebugEnabled()) {
            log.debug("Found resource: " + name);
          }
          // Trim leading slash
          resources.add(name.substring(1));
        }

那么可以猜测一下,可以修改成我们需要的,例如我想查找包含.md为后缀的文件的路径,那么就可以修改成:

代码语言:txt
AI代码解释
复制
// Check file name
if (name.indexOf(path) > 0) {
    if (log.isDebugEnabled()) {
        log.debug("Found resource: " + name);
    }
    // Trim leading slash
    resources.add(name.substring(1));
}

可以看到,我们最终想要的,可以模仿的有两个核心的java类:

ResolverUtil.java

VFS.java

这两个类,刚好是Mybatis的i/o模块中的:

二话不说,就拷贝到了我开发的框架的项目中:

1608462861538-6d536665-6ecf-4e27-a3d7-0f3ad1ac5ef2.png
1608462861538-6d536665-6ecf-4e27-a3d7-0f3ad1ac5ef2.png

六、修改成自己想要的

ResolverUtil中的find方法:

代码语言:txt
AI代码解释
复制
  /**
   * 查找包下的资源
   * @param packageName
   * @return
   */
  public ResolverUtil find(String packageName) {
    //把com.truedei  形式的路径 变成:com/truedei
    String path = getPackagePath(packageName);

    try {

      List<String> children = VFS.getInstance().list(path);

      for (String child : children) {

        if (child.endsWith(".class")) {
          //组装成一个新的文件路径
          child = "/md"+child.substring(child.lastIndexOf('/')).replace("class","md");
          //child就是我想要加载的md文件的路径
          loadJarFileByFile(child);
        }

      }
    } catch (IOException ioe) {
      System.out.println("Could not read package: " + packageName+"-->"+ioe);
    }

    return this;
  }

自己写的:

代码语言:txt
AI代码解释
复制
 /**
   * 加载jar包的资源文件
   * @param file
   * @return
   */
  private  void  loadJarFileByFile(String file) {
    InputStream stream = this.getClass().getResourceAsStream(file);

    if(stream==null){
      return ;
    }

    BufferedReader br = null ;
    try {
      br = new BufferedReader(new InputStreamReader(stream,"UTF-8")) ;
      String s=null ;
      while((s=br.readLine()) !=null){
        //把每一行数据都添加到全局的List中后期对其方便处理
        fileContentList.add(s);
      }
      br.close();
    } catch (FileNotFoundException e) {
      System.out.println("FileNotFoundException:"+e);
    } catch (IOException e) {
      System.out.println("IOException :"+e);
    }finally {
      if(br !=null){
        try {
          br.close();
        } catch (IOException e) {
          System.out.println("close br error:"+e);
        }
      }
    }
  }

七、成功的喜悦

1608463384219-6e39deaf-f9c9-4c19-aea5-68eec473adb6.png
1608463384219-6e39deaf-f9c9-4c19-aea5-68eec473adb6.png

可以看到我想解析的md文件的数据也都被解析出来了:

1608463434842-4d1ef5ef-0a2b-4abc-bea8-49d7941fa022.png
1608463434842-4d1ef5ef-0a2b-4abc-bea8-49d7941fa022.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Mybatis3.2扫描ant通配符格式的typeAliasPackage
业务场景:首先项目进行分布式拆分之后,按照模块再分为为api层和service层,web层。 其中订单业务的实体类放在com.muses.taoshop.item.entity,而用户相关的实体类放在com.muses.taoshop.user.entity。所以就这样,通过通配符方式去setTypeAliasesPackage ,com.muses.taoshop.*.entity
SmileNicky
2019/01/17
1.8K0
Sping Boot集成MyBatis打包成jar时,找不到类的问题
在Spring Boot中,由于是嵌套Jar,导致Mybatis默认的VFS实现DefaultVFS无法扫描嵌套Jar中的类。
用户1499526
2019/07/15
1.5K0
Mybatis源码解析(八):Mapper代理原理
冬天vs不冷
2025/01/21
900
Mybatis源码解析(八):Mapper代理原理
Mybatis源码分析
variables:用来存放 properties 节点中解析出来的 Properties 数据。
用户7353950
2022/06/23
4790
Mybatis源码分析
MyBatis-01 MyBatis入门篇
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
小小工匠
2021/08/17
3240
mybatis源码解读(二)——构建Configuration对象
  Configuration 对象保存了所有mybatis的配置信息,主要包括:   ①、 mybatis-configuration.xml 基础配置文件   ②、 mapper.xml 映射器配置文件 1、读取配置文件   前面例子有这么一段代码: 1 private static SqlSessionFactory sqlSessionFactory; 2 3 static{ 4 InputStream inputStream = MybatisTest.class
IT可乐
2018/05/28
1.3K0
MyBatis 源码分析篇---配置文件的解析过程(二)
今天我们接着来看看其他常用属性的解析过程,重点介绍typeAliases,environments等配置的解析。
码农飞哥
2021/08/18
3070
几百行代码写个Mybatis,原理搞的透透的!
因为我们在使用 Mybatis 的时候,只需要定义一个不需要写实现类的接口,就能通过注解或者配置SQL语句的方式,对数据库进行 CRUD 操作。
小傅哥
2021/08/02
6020
几百行代码写个Mybatis,原理搞的透透的!
Mybatis
从xml配置文件中读取配置,然后通过SqlSessionFactoryBuilder构建SqlSessionFactory实例(建造者模式)。SqlSessionFactory是Mybatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像。SqlSessionFactory是创建SqlSession的工厂,每一个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心,同时SqlSessionFactory也是线程安全的,SqlSessionFactory一旦被创建,在应用执行期间都存在。
spilledyear
2018/08/21
1.4K0
Mybatis
Mybatis源码阅读(一) 配置文件的加载及查询过程
首先在 MyBatis 启动的时候我们要去解析配置文件,包括全局配置文件和映射器配置文件,这里面包含了我们怎么控制 MyBatis 的行为,和我们要对数据库下达的指令,也就是我们的 SQL 信息。我们会把它们解析成一个 Configuration 对象。
源码之路
2021/12/16
9490
Mybatis源码阅读(一) 配置文件的加载及查询过程
MyBatis源码解析(一)——MyBatis初始化过程解析
1. 准备工作 为了看清楚MyBatis的整个初始化过程,先创建一个简单的Java项目,目录结构如下图所示: 1.1 Product 产品实体类 public class Product {
大闲人柴毛毛
2018/03/12
8760
MyBatis源码解析(一)——MyBatis初始化过程解析
从源码的角度分析mybatis的核心流程(上)
mybatis可以说是目前互联网公司使用最广泛半自动的ORM框架,它不仅能够替代我们编写繁琐的JDBC代码,而且手动编写sql可以编写出更高性能的sql语句。这么优秀的开源框架,我觉得我们应该学习一下。
全栈程序员站长
2022/07/04
5420
从源码的角度分析mybatis的核心流程(上)
MyBatis详解(一)
【1】MyBatis是一个持久层的ORM框架【Object Relational Mapping,对象关系映射】,使用简单,学习成本较低。可以执行自己手写的SQL语句,比较灵活。但是MyBatis的自动化程度不高,移植性也不高,有时从一个数据库迁移到另外一个数据库的时候需要自己修改配置,所以称只为半自动ORM框架。
忧愁的chafry
2022/12/02
6600
Mybatis初始化的builder建造者模式
在Mybatis的初始化的主要工作是加载并解析mybatis-config.xml的配置文件、映射配置文件以及相关的注解信息。因为使用了建造者模式,BashBuilder抽象类即为建造者接口的角色。它的核心字段内容如下
算法之名
2019/08/20
2.2K0
Mybatis初始化的builder建造者模式
源码分析(1.4万字) | Mybatis接口没有实现类为什么可以执行增删改查
MyBatis 是一款非常优秀的持久层框架,相对于IBatis更是精进了不少。与此同时它还提供了很多的扩展点,比如最常用的插件;语言驱动器,执行器,对象工厂,对象包装器工厂等等都可以扩展。那么,如果想成为一个有深度的男人(程序猿),还是应该好好的学习一下这款开源框架的源码,以此可以更好的领会设计模式的精髓(面试?)。其实可能平常的业务开发中,并不会去深究各个框架的源代码,也常常会听到即使不会也可以开发代码。但!每个人的目标不同,就像;代码写的好工资加的少(没有bug怎么看出你工作嘞!),好!为了改变世界,开始分析喽!
小傅哥
2020/07/14
1K0
源码分析(1.4万字) | Mybatis接口没有实现类为什么可以执行增删改查
Mybatis-mapper-xml-基础
今天学习http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html。关于mapper.xml的sql语句的使用。 项目路径:https://github.c
Ryan-Miao
2018/03/13
1.8K0
Mybatis-mapper-xml-基础
初始MyBatis(随笔1)
为什么要学习框架技术: PTT模板举例:做ppt前, 总是会需要找一些模板, 优点; 1. 不用考虑布局, 排版等问题, 提高了效率; 2. 可以专心于PTT的内容, 使演讲 “质量” 更有保障; 3. 新手也可以实现出很好的 PPT; 框架:也是基于这样的考虑, 当确定使用哪个技术框架后, 就相当于有一个 “半成品” 项目框架, 在编写内部 增删改查代码即可… 1. 不用在考虑公共问题, 框架已经帮我们做好了; 不同公司,不同的人写代码都会有所不同... 2. 可以专心于业务逻辑,保证核心业务逻辑的开发质量; 3. 结构统一,便于学习维护; 4. 框架集成了前人的经验,可以帮助新手写出稳定,性能优良,结构优美的高质量程序; 框架的概念 框架的强大之处不是它可以让你,做什么而是,它不能让你做什么; —————— Rickard Oberg 一千个人心中有一千个哈姆雷特,框架使混乱的东西变大结构化,一千个人可以写出一千种 Servlet+JSP+JavaBean… 主流框架的介绍
Java_慈祥
2024/08/06
1450
初始MyBatis(随笔1)
MyBatis入门到源码分析 | 【Mybatis加载核心配置文件流程】
resources目录下创建:mybatis-config.xml,并写入以下内容:
青山师
2023/05/05
2980
源码分析Mybatis MappedStatement的创建流程
上文重点阐述源码分析MapperProxy初始化,但并没有介绍.Mapper.java(UserMapper.java)是如何与Mapper.xml文件中的SQL语句是如何建立关联的。本文将重点接开这个谜团。
丁威
2019/06/11
1.6K0
源码分析Mybatis MappedStatement的创建流程
【云+社区年度征文】从零开始搭建一个SpringBoot应用并成功上云
相信大家都具有在本地编写项目的丰富经验,然而本地的单击项目始终不能满足我们的需求,为此,本篇文章将介绍如何编写一个SpringBoot应用并成功将其部署到云服务器上。
wangweijun
2020/11/25
6700
相关推荐
Mybatis3.2扫描ant通配符格式的typeAliasPackage
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档