【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理
【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理
目录
本文导读
一、MyBatis中运用的设计模式详解
1. 工厂模式(Factory Pattern)
2. 单例模式(Singleton Pattern)
3. 建造者模式(Builder Pattern)
4. 代理模式(Proxy Pattern)
5. 模板方法模式(Template Method Pattern)
6. 策略模式(Strategy Pattern)
7. 观察者模式(Observer Pattern)
二、MyBatis设计模式的源码实现原理
工厂模式的应用 - SqlSessionFactory
单例模式的应用 - SqlSessionFactory
代理模式的应用 - Mapper动态代理
建造者模式(Builder Pattern)
策略模式 - Strategy Pattern
观察者模式 - Observer Pattern
三、MyBatis源码解析
工厂模式 - SqlSessionFactoryBuilder
单例模式 - SqlSessionFactory
代理模式 - MapperProxyFactory
总结
博主v:XiaoMing_Java
本文导读
MyBatis是一个流行的持久层框架,它极大地简化了数据库操作,提升了开发效率。在其底层实现中,MyBatis广泛采用了各种设计模式,这些设计模式不仅增加了框架的灵活性和可维护性,也为开发人员提供了丰富的使用场景和扩展能力。本文将深入探讨MyBatis所应用的主要设计模式及其在框架中的具体应用。
一、MyBatis中运用的设计模式详解
1. 工厂模式(Factory Pattern)
工厂模式是一种创建型模式,它提供了一种创建对象的最佳方式。在MyBatis中,SqlSessionFactoryBuilder
、SqlSessionFactory
等关键组件的创建过程就运用到了工厂模式。尤其是SqlSessionFactory
的创建,它是通过SqlSessionFactoryBuilder
读取MyBatis配置文件并构建出SqlSessionFactory
实例。这样做可以隔离复杂的初始化过程,使用户只需关注最终产出,而无需了解创建实例的复杂过程。
2. 单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在MyBatis中,SqlSessionFactory
的设计就是单例模式的经典应用。一旦通过SqlSessionFactoryBuilder
创建了SqlSessionFactory
实例后,该实例就会在应用程序中被复用,避免了重复创建实例带来的资源浪费。
3. 建造者模式(Builder Pattern)
建造者模式旨在将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。MyBatis中的XMLConfigBuilder
、XMLMapperBuilder
等类的设计采用了建造者模式。这些Builder类负责解析MyBatis的配置文件和映射文件,逐步构建出配置信息和映射信息。通过建造者模式,MyBatis将复杂的解析过程分解成一系列的步骤,使得代码更加清晰和易于维护。
4. 代理模式(Proxy Pattern)
代理模式为其他对象提供一个代理以控制对这个对象的访问。MyBatis中对Mapper接口的实现就是基于JDK动态代理机制。当调用Mapper接口的方法时,实际上是由MyBatis生成的代理类去执行。这种方式允许MyBatis在执行Mapper方法前后插入自定义逻辑,如开启事务、处理缓存等,从而大幅提升了灵活性和可扩展性。
5. 模板方法模式(Template Method Pattern)
模板方法模式定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。MyBatis的BaseExecutor
类就是一个模板方法模式的例证。它定义了数据库操作的基本流程,如查询、更新、提交事务等,而具体的执行逻辑则留给其子类(比如SimpleExecutor
、BatchExecutor
等)去实现。这样做的好处是复用了代码,同时又保留了灵活性。
6. 策略模式(Strategy Pattern)
策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。MyBatis中的缓存策略、加载策略等正是策略模式的应用。例如,MyBatis允许用户配置不同的缓存实现(如EHCache、OSCache等),并在运行时根据配置动态选择。这种模式使得MyBatis具有很高的灵活性和扩展性。
7. 观察者模式(Observer Pattern)
观察者模式定义了一种依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在MyBatis中,Configuration
对象就是一个观察者,它会监听映射文件的解析事件,并在解析完成后更新自己的状态。通过观察者模式,MyBatis能够动态响应配置信息的变化,增强了框架的动态性和灵活性。
以上便是MyBatis中常见的几种设计模式及其应用。这些设计模式的运用大大提升了MyBatis的内聚性、可扩展性和灵活性,使得MyBatis成为了Java领域广泛使用的ORM框架之一。
二、MyBatis设计模式的源码实现原理
在第一部分中,我们讨论了MyBatis使用的一些关键设计模式及其作用。现在,我们将深入探索这些设计模式在MyBatis源码中的具体实现原理。
工厂模式的应用 - SqlSessionFactory
实现原理:
SqlSessionFactory
的创建是通过SqlSessionFactoryBuilder
完成的。这个过程遵循了典型的工厂模式设计,SqlSessionFactoryBuilder
充当工厂的角色,负责生产SqlSessionFactory
对象。
public SqlSessionFactory build(Reader reader) {// 使用XMLConfigBuilder解析配置文件XMLConfigBuilder parser = new XMLConfigBuilder(reader, null, null);return build(parser.parse());
}
在上述代码中,build
方法首先通过XMLConfigBuilder
解析给定的配置文件,然后根据解析结果构建出一个SqlSessionFactory
实例。这个过程封装了对象的创建逻辑,使得客户端代码无需直接与对象的创建细节打交道。
单例模式的应用 - SqlSessionFactory
实现原理:
在MyBatis中,通常我们会将SqlSessionFactory
作为单例存在,以保证全局只有一个数据库连接池,从而节省资源。、
public class MyBatisUtil {private static SqlSessionFactory sqlSessionFactory;static {try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {throw new ExceptionInInitializerError(e);}}public static SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;}
}
在这段代码中,利用静态初始化块加载配置并创建SqlSessionFactory
,确保了其单例性。此外,通过提供一个静态方法getSqlSessionFactory
来全局访问该实例,进一步体现了单例模式的特点。
代理模式的应用 - Mapper动态代理
实现原理:
MyBatis中对Mapper接口的实现是基于JDK动态代理的。当调用Mapper接口的方法时,实际上是委托给了由MyBatis动态生成的代理类。
public class MapperProxy<T> implements InvocationHandler {private final SqlSession sqlSession;private final Class<T> mapperInterface;public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法调用的处理逻辑if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}// 执行SQL操作return sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());}
}
建造者模式(Builder Pattern)
MyBatis在处理配置文件时,使用了建造者模式。XMLConfigBuilder
和XMLMapperBuilder
是两个具体的例子,它们负责解析MyBatis的配置文件和映射文件。
实现原理:这段代码展示了BaseExecutor
中的query
方法,这是一个模板方法,它定义了执行查询的基本流程,并将具体的查询逻辑委托给doQuery
方法。子类需要根据不同的需求实现doQuery
方法,比如SimpleExecutor
、ReuseExecutor
等都有各自的实现,这正是模板方法模式的典型应用。
public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private final XPathParser parser;public XMLConfigBuilder(InputStream inputStream) {this(new XPathParser(inputStream, true, null));}public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;// 省略解析过程...return configuration;}
}
策略模式 - Strategy Pattern
MyBatis的缓存策略使用了策略模式。MyBatis允许用户选择或自定义缓存实现,这是通过将缓存行为抽象成接口,并允许动态设置具体实现来实现的。
实现原理:Cache
接口定义了缓存的行为,而PerpetualCache
提供了Cache
接口的一个基础实现。MyBatis还支持更多缓存实现,如OSCache、Ehcache等,开发人员可以根据需要选择或自定义缓存策略,这体现了策略模式的灵活性。
public interface Cache {void putObject(Object key, Object value);Object getObject(Object key);// 更多方法...
}public class PerpetualCache implements Cache {private Map<Object, Object> cache = new HashMap<>();@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}// 实现其他方法...
}
观察者模式 - Observer Pattern
MyBatis利用观察者模式来实现插件功能。插件可以在MyBatis操作数据库的关键节点被插入,比如执行查询之前。这种机制使得用户能够在不修改MyBatis核心代码的情况下,扩展其功能。
实现原理:Interceptor
接口定义了插件需要实现的intercept
方法。SomePlugin
是一个具体的插件实现,它可以在方法调用前后执行额外的逻辑。这种方式使得MyBatis能够在运行时灵活地扩展功能,体现了观察者模式的特点。
public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;// 更多方法...
}public class SomePlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 在执行前后添加额外的操作Object returnObject = invocation.proceed();// 在执行后处理return returnObject;}
}
三、MyBatis源码解析
深入探讨MyBatis的源码对于理解其设计模式的应用至关重要。由于篇幅限制,我们将通过几个关键组件的源码片段来揭示MyBatis是如何实现上文提到的设计模式的。请注意,下面的代码是简化版本,旨在帮助理解其核心原理。
工厂模式 - SqlSessionFactoryBuilder
MyBatis通过SqlSessionFactoryBuilder
创建SqlSessionFactory
的过程是工厂模式的一个典型应用。
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(Reader reader) {// 解析配置文件,构建Configuration对象XMLConfigBuilder parser = new XMLConfigBuilder(reader);Configuration config = parser.parse();// 返回SqlSessionFactory实例return new DefaultSqlSessionFactory(config);}
}
单例模式 - SqlSessionFactory
虽然MyBatis不直接提供SqlSessionFactory
的单例实现,但在实际应用中,开发者通常会将其实现为单例模式,以下是一个简单的示例:
public class MyBatisUtil {private static SqlSessionFactory sqlSessionFactory;static {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}public static SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;}
}
代理模式 - MapperProxyFactory
MyBatis使用JDK动态代理为Mapper接口生成代理对象,以便拦截接口方法的调用。
public class MapperProxy<T> implements InvocationHandler {private final SqlSession sqlSession;private final Class<T> mapperInterface;public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 省略具体实现return sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());}
}public class MapperProxyFactory<T> {private final Class<T> mapperInterface;public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public T newInstance(SqlSession sqlSession) {MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);}
}
总结
每个设计模式的实现都体现了MyBatis设计的巧妙和优雅,通过这些模式的应用,MyBatis成功地将框架的灵活性、可扩展性和维护性提升到了一个新的高度。希望通过这三部分的解析,你能对MyBatis的设计和实现有一个更加深入的理解。
如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续创作!!!
博主v:XiaoMing_Java
📫作者简介:嗨,大家好,我是 小明 ,互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
🔥Redis从入门到精通与实战🔥
Redis从入门到精通与实战
围绕原理源码讲解Redis面试知识点与实战
🔥MySQL从入门到精通🔥
MySQL从入门到精通
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
深入理解计算机系统CSAPP
以深入理解计算机系统为基石,构件计算机体系和计算机思维
Linux内核源码解析
围绕Linux内核讲解计算机底层原理与并发
🔥数据结构与企业题库精讲🔥
数据结构与企业题库精讲
结合工作经验深入浅出,适合各层次,笔试面试算法题精讲
🔥互联网架构分析与实战🔥
企业系统架构分析实践与落地
行业最前沿视角,专注于技术架构升级路线、架构实践
互联网企业防资损实践
互联网金融公司的防资损方法论、代码与实践
🔥Java全栈白宝书🔥
精通Java8与函数式编程
本专栏以实战为基础,逐步深入Java8以及未来的编程模式
深入理解JVM
详细介绍内存区域、字节码、方法底层,类加载和GC等知识
深入理解高并发编程
深入Liunx内核、汇编、C++全方位理解并发编程
Spring源码分析
Spring核心七IOC/AOP等源码分析
MyBatis源码分析
MyBatis核心源码分析
Java核心技术
只讲Java核心技术
相关文章:

【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理
【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理 目录 本文导读 一、MyBatis中运用的设计模式详解 1. 工厂模式(Factory Pattern) 2. 单例模式(Singleton Pattern) 3. 建造者模式(Bu…...

Acrel-1000DP光伏监控系统在尚雷仕(湖北)健康科技有限公司5.98MW分布式光伏10KV并网系统的应用
摘 要:分布式光伏发电特指在用户场地附近建设,运行方式多为自发自用,余电上网,部分项目采用全额上网模式。分布式光伏全额上网的优点是可以充分利用分布式光伏发电系统的发电量,提高分布式光伏发电系统的利用率。发展分…...

电脑远程控制esp32上的LED
1、思路整理 首先esp32需要连接上wifi 然后创建udp socket 接受udp数据 最后解析数据,控制LED 2、micropython代码实现 import network from socket import * from machine import Pin p2Pin(2,Pin.OUT)def do_connect(): #连接wifi wlan network.WLAN(network.…...

ARXML处理 - C#的解析代码(一)
目的 本文介绍通过AUTOSAR组织提供的xsd文件,自动生成对应的C#解析代码的框架。 自动生成方法:Microsoft SDKs\Windows\v7.0A\bin\xsd.exe 命令:xsd.exe AUTOSAR_4-0-3.xsd /c /l:CS /n:AUTOSAR4 AUTOSAR_4-0-3.xsd 是需要生成代码的xsd文…...

OJ 栓奶牛【C】【Python】【二分算法】
题目 算法思路 要求的距离在最近木桩与最远木桩相隔距离到零之间,所以是二分法 先取一个中间值,看按照这个中间值可以栓多少奶牛,再与输入奶牛数比较,如果大于等于,则增大距离,注意这里等于也是增大距离…...

Spring6-单元测试:JUnit
1. 概念 在进行单元测试时,特别是针对使用了Spring框架的应用程序,我们通常需要与Spring容器交互以获取被测试对象及其依赖。传统做法是在每个测试方法中手动创建Spring容器并从中获取所需的Bean。以下面的两行常见代码为例: ApplicationCo…...

ubuntu系统安装k8s1.28精简步骤
目录 一、规划二、环境准备2.1 配置apt仓库配置系统基本软件仓库配置k8s软件仓库安装常用软件包 2.2 修改静态ip、ntp时间同步、主机名、hosts文件、主机免密2.3 内核配置2.4 关闭防火墙、selinux、swap2.5 安装软件安装docker安装containerd安装k8s软件包 三、安装配置k8s3.1 …...

探讨Java和Go语言的缺点
文章目录 Java的缺点Go语言的缺点 通常我们都会讨论Java和GO的优点,如果讨论缺点往往能让人们更清楚优点的重要性,Java和Go的缺点或许往往就是对方优点所在 Java的缺点 冗长的代码:相较于一些现代编程语言,Java 的语法相对冗长&am…...

短剧在线搜索PHP网站源码
源码简介 短剧在线搜索PHP网站源码,自带本地数据库500数据,共有6000短剧视频,与短剧猫一样。 搭建环境 PHP 7.3 Mysql 5.6 安装教程 1.上传源码到网站目录中 2.修改【admin.php】中, $username ‘后台登录账号’; $passwor…...

Python map遍历
在Python中,map 函数是一个内置函数,它将指定的函数应用于给定序列(如列表、元组等)的每个项,并返回一个迭代器,该迭代器包含所有项经过指定函数处理后的结果。 ### map 函数的基本用法 map 函数的语法如…...

数据结构—红黑树
红黑树介绍 红黑树(Red Black Tree)是一种自平衡二叉查找树。由于其自平衡的特性,保证了最坏情形下在 O(logn) 时间复杂度内完成查找、增加、删除等操作,性能表现稳定。 在 JDK 中,TreeMap、TreeSet 以及 JDK1.8 的 …...

MES实施之工控机和电脑的选择
在MES项目实施过程中,经常会碰到工控机和电脑的选型问题,那么他们的区别是什么? 1、控机和普通个人电脑(PC)相比,具有以下几个区别: 1.运行环境不同:工控机通常需要在各种恶劣的工业环境中运行,如高温、高湿、强电磁干扰等,因此需要具有防尘、防水、抗干扰等特点。而…...

京东云服务器4核8G主机租用价格418元一年,1899元3年
京东云轻量云主机4核8G服务器租用价格418元一年,1899元3年,配置为:轻量云主机4C8G-180G SSD系统盘-5M带宽-500G月流量,京东云主机优惠活动 yunfuwuqiba.com/go/jd 可以查看京东云服务器详细配置和精准报价单,活动打开如…...

【多模态融合】MetaBEV 解决传感器故障 3D检测、BEV分割任务
前言 本文介绍多模态融合中,如何解决传感器故障问题;基于激光雷达和相机,融合为BEV特征,实现3D检测和BEV分割,提高系统容错性和稳定性。 会讲解论文整体思路、模型框架、论文核心点、损失函数、实验与测试效果等。 …...

[通俗易懂]《动手学强化学习》学习笔记1-第1章 初探强化学习
文章目录 前言第1章 初探强化学习1.1 简介序贯决策(sequential decision making)任务:强化学习与有监督学习或无监督学习的**区别**:改变未来 1.2 什么是强化学习环境交互与有监督学习的区别1:改变环境 (说…...

centOS如何升级python
centOS下升级python版本的详细步骤 1、可利用linux自带下载工具wget下载,如下所示: 笔者安装的是最小centos系统,所以使用编译命令前,必须安装wget服务,读者如果安装的是界面centos系统,或者使用过编译工具…...

【MYSQL锁】透彻地理解MYSQL锁
🔥作者主页:小林同学的学习笔录 🔥mysql专栏:小林同学的专栏 目录 1.锁 1.1 概述 1.2 全局锁 1.2.1 语法 1.2.1.1 加全局锁 1.2.1.2 数据备份 1.2.1.3 释放锁 1.2.1.4 特点 1.2.1.5 演示 1.3 表级锁 1.3.1 介绍 …...

【静态分析】静态分析笔记01 - Introduction
参考: BV1zE411s77Z [南京大学]-[软件分析]课程学习笔记(一)-introduction_南京大学软件分析笔记-CSDN博客 ------------------------------------------------------------------------------------------------------ 1. program language and static analysis…...

使用的sql
根据CODE去重 SELECT * FROM ( SELECT count( camera_code ) AS count, camera_code FROM n_camera_basic GROUP BY camera_code ) t WHERE t.count >1 DELETE FROM n_camera_basic WHERE camera_id NOT IN (SELECT dt.minno…...

【ZZULIOJ】1052: 数列求和4(Java)
目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 输入n和a,求aaaaaa…aa…a(n个a),如当n3,a2时,222222的结果为246 输入 包含两个整数,n和a,含义如上述,你可以假定n和a都是小于10的非负整…...

【Linux】tcpdump P3 - 过滤和组织返回信息
文章目录 基于TCP标志的过滤器格式化 -X/-A额外的详细选项按协议(udp/tcp)过滤低详细输出 -q时间戳选项 本文继续展示帮助你过滤和组织tcpdump返回信息的功能。 基于TCP标志的过滤器 可以根据各种TCP标志来过滤TCP流量。这里是一个基于tcp-ack标志进行过滤的例子。 # tcpdump…...

vscode免费登录ssh ,linux git配置免密码
1、vscode远程ssh免密 在windows下生成密钥 , cmd窗口下执行 ssh-keygen -t rsa 在C:\Users\xxxx\.ssh目录下生成 在linux下面 cd .ssh 创建authorized_keys 文件, 把之前windows下生成的 id_rsa.pub内容复制进去 2、gitlab 配置。 在linux下面 ssh-keygen -t rs…...

Netty 心跳(heartbeat)——服务源码剖析(上)(四十一)
剖析目的 Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一个服务----心跳机制 heartbeat.通过心跳检査对方是否有效,这是 RPC 框架中是必不可少的功能。下面我们分析一下 Netty 内部心跳服务源码实现。 源…...

C语言—每日选择题—Day65
前言 我们的刷题专栏又又又开始了,本专栏总结了作者做题过程中的好题和易错题。每道题都会有相应解析和配图,一方面可以使作者加深理解,一方面可以给大家提供思路,希望大家多多支持哦~ 第一题 1、如下代码输出的是什么…...

【环境变量】基本概念理解 | 查看环境变量echo | PATH的应用和修改
目录 前言 基本概念&理解 注意的点 查看环境变量的方法 PATH环境变量 PTAH应用系统指令 PTAH应用用户程序 命令行参数的修改(内存级) 配置文件的修改 windows环境变量 大家天天开心🙂 bash进程的流程。环境变量在系统指…...

5.7Python之元组
元组(Tuple)是Python中的一种数据类型,它是一个有序的、不可变的序列。元组使用圆括号 () 来表示,其中的元素可以是任意类型,并且可以包含重复的元素。 与列表(List)不同,元组是不可…...

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之一 简单视频放大抖动效果
Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之一 简单视频放大抖动效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之一 简单视频放大抖动效果 一、简单介绍 二、简单视频放大抖动效果实现原理 三、简单视频放大…...

如何通过VPN访问内网?
VPN(Virtual Private Network)是一种通过公共网络建立私有网络连接的技术,可以在不同地点的网络中建立安全通道,实现远程访问内网资源的目的。本文将介绍如何通过VPN访问内网,并介绍一款名为“天联”的VPN服务。 什么是…...

RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表
RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表 文章目录 RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表1. MQTT概览2. MQTT 5.0 特性1. 特性概要2. Docker中安装RabbitMQ及启用MQTT5.0协议 3. MQTT 5.0 功能列表1. 消息过期1. 描述2. 举例3. 实现 2. 订阅标识…...

常用脚本01 - 生成证书
1 生成证书 第一步、准备脚本文件 [rootharbor-01 ssl]# vim gencert.sh #!/usr/bin/env bash set -eDOMAIN"$1" IP"$2" WORK_DIR"$(mktemp -d)"if [ -z "$DOMAIN" ]; thenecho "Domain name needed."exit 1 fiecho "…...