首页 > 其他分享 >mybatis学习第⼗⼀部分:mybatis使用到的设计模式

mybatis学习第⼗⼀部分:mybatis使用到的设计模式

时间:2022-11-23 23:12:13浏览次数:37  
标签:return String Builder 代理 模式 学习 mybatis 设计模式 public

虽然我们都知道有3类23种设计模式,但是⼤多停留在概念层⾯,Mybatis源码中使⽤了⼤量的设计模
式,观察设计模式在其中的应⽤,能够更深⼊的理解设计模式
Mybatis⾄少⽤到了以下的设计模式的使⽤:

模式

mybatis 体现

Builder

模式

 

例如SqlSessionFactoryBuilder、Environment;

⼯⼚⽅ 法模式

 

例如SqlSessionFactory、TransactionFactory、LogFactory

单例模 式

 

例如 ErrorContext 和  LogFactory;

代理模 式

Mybatis实现的核⼼,⽐如MapperProxy、ConnectionLogger,⽤的jdk的动态代理 还有executor.loader包使⽤了   cglib或者javassist达到延迟加载的效果

组合模 式

 

例如SqlNode和各个⼦类ChooseSqlNode等;

模板⽅ 法模式

例如 BaseExecutor 和 SimpleExecutor,还有 BaseTypeHandler 和所有的⼦类例如

IntegerTypeHandler;

适配器 模式

 

例如Log的Mybatis接⼝和它对jdbc、log4j等各种⽇志框架的适配实现;

装饰者 模式

 

例如Cache包中的cache.decorators⼦包中等各个装饰者的实现;

迭代器 模式

 

例如迭代器模式PropertyTokenizer;

 

接下来对Builder构建者模式、⼯⼚模式、代理模式进⾏解读,先介绍模式⾃身的知识,然后解读在

Mybatis中怎样应⽤了该模式。

11.1 Builder构建者模式

Builder模式的定义是"将⼀个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表 示。”,它属于创建类模式,⼀般来说,如果⼀个对象的构建⽐较复杂,超出了构造函数所能包含的范 围,就可以使⽤⼯⼚模式和Builder模式,相对于⼯⼚模式会产出⼀个完整的产品,Builder应⽤于更加 复杂的对象的构建,甚⾄只会构建产品的⼀个部分,直⽩来说,就是使⽤多个简单的对象⼀步⼀步构建 成⼀个复杂的对象

例⼦:使⽤构建者设计模式来⽣产computer 主要步骤:

1、将需要构建的⽬标类分成多个部件(电脑可以分为主机、显示器、键盘、⾳箱等部件);

2、 创建构建类;

3、 依次创建部件;

4、 将部件组装成⽬标对象

1.  定义computer

package com.lagou.dao;


import org.apache.ibatis.binding.BindingException; import org.apache.ibatis.session.SqlSession;

import java.util.Optional;


public class Computer { private String displayer; private String mainUnit; private String mouse; private String keyboard;

public String getDisplayer() { return displayer;
}
public void setDisplayer(String displayer) { this.displayer = displayer;
}
public String getMainUnit() { return mainUnit;
}
public void setMainUnit(String mainUnit) { this.mainUnit = mainUnit;
}
public String getMouse() { return mouse;
}
public void setMouse(String mouse) { this.mouse = mouse;
}
public String getKeyboard() { return keyboard;
}
public void setKeyboard(String keyboard) { this.keyboard = keyboard;
}
@Override
public String toString() {
return "Computer{" + "displayer='" + displayer + '\'' + ", mainUnit='"
+ mainUnit + '\'' + ", mouse='" + mouse + '\'' + ", keyboard='" + keyboard + '\'' + '}';
}

ComputerBuilder

public static class ComputerBuilder {
private ComputerBuilder target = new ComputerBuilder();
public Builder installDisplayer(String displayer) {
target.setDisplayer(displayer);
return this;
}
public Builder installMainUnit(String mainUnit) {
target.setMainUnit(mainUnit);
return this;
}
public Builder installMouse(String mouse) {
target.setMouse(mouse);
return this;
}
public Builder installKeybord(String keyboard) {
target.setKeyboard(keyboard);
return this;
}
public ComputerBuilder build() {
return target;
}
}

调⽤

public static void main(String[]args){
ComputerBuilder computerBuilder=new ComputerBuilder();
computerBuilder.installDisplayer("显万器");
computerBuilder.installMainUnit("主机");
computerBuilder.installKeybord("键盘");
computerBuilder.installMouse("⿏标");
Computer computer=computerBuilder.Builder();
System.out.println(computer);
}

Mybatis中的体现
SqlSessionFactory 的构建过程:
Mybatis的初始化⼯作⾮常复杂,不是只⽤⼀个构造函数就能搞定的。所以使⽤了建造者模式,使⽤了
⼤ 量的Builder,进⾏分层构造,核⼼对象Configuration使⽤了 XmlConfigBuilder来进⾏构造

在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调⽤XMLConfigBuilder读取所有的
MybatisMapConfig.xml 和所有的 *Mapper.xml ⽂件,构建 Mybatis 运⾏的核⼼对象 Configuration
对 象,然后将该Configuration对象作为参数构建⼀个SqlSessionFactory对象。

private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析<properties />标签
propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载⾃定义的VFS实现类
loadCustomVfs(settings);
// 解析 <typeAliases /> 标签
typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins />标签
pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 标签
objectFactoryElement(root.evaINode("obj ectFactory"));
// 解析 <objectWrapper Factory /> 标签
obj ectWrappe rFacto ryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 <settings /> 到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 标签
databaseldProviderElement(root.evalNode("databaseldProvider"));
}

其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调⽤ XMLMapperBuilder ⽤于读取
*Mapper ⽂件,⽽XMLMapperBuilder会使⽤XMLStatementBuilder来读取和build所有的SQL语句。

//解析<mappers />标签
mapperElement(root.evalNode("mappers"));

在这个过程中,有⼀个相似的特点,就是这些Builder会读取⽂件或者配置,然后做⼤量的XpathParser
解析、配置或语法的解析、反射⽣成对象、存⼊结果缓存等步骤,这么多的⼯作都不是⼀个构造函数所
能包括的,因此⼤量采⽤了 Builder模式来解决

SqlSessionFactoryBuilder类根据不同的输⼊参数来构建SqlSessionFactory这个⼯⼚对象

11.2 ⼯⼚模式

在Mybatis中⽐如SqlSessionFactory使⽤的是⼯⼚模式,该⼯⼚没有那么复杂的逻辑,是⼀个简单⼯⼚     模式。

简单⼯⼚模式(Simple Factory Pattern):⼜称为静态⼯⼚⽅法(Static Factory Method)模式,它属于创 建型模式。

在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。简单⼯⼚模式专⻔定义⼀个类来负责创建 其他类的实例,被创建的实例通常都具有共同的⽗类

例⼦:⽣产电脑

假设有⼀个电脑的代⼯⽣产商,它⽬前已经可以代⼯⽣产联想电脑了,随着业务的拓展,这个代⼯⽣产 商还要⽣产惠普的电脑,我们就需要⽤⼀个单独的类来专⻔⽣产电脑,这就⽤到了简单⼯⼚模式。

下⾯我们来实现简单⼯⼚模式:

1. 创建抽象产品类 我们创建⼀个电脑的抽象产品类,他有⼀个抽象⽅法⽤于启动电脑:

public abstract class Computer {
/**
*产品的抽象⽅法,由具体的产品类去实现
*/
public abstract void start();
}

2. 创建具体产品类
接着我们创建各个品牌的电脑,他们都继承了他们的⽗类Computer,并实现了⽗类的start⽅法:

public class LenovoComputer extends Computer{
@Override
public void start() {
System.out.println("联想电脑启动");
}
public class HpComputer extends Computer{
@Override
public void start() {
System.out.println("惠普电脑启动");
}
}

3. 创建⼯⼚类
接下来创建⼀个⼯⼚类,它提供了⼀个静态⽅法createComputer⽤来⽣产电脑。你只需要传⼊你
想⽣ 产的电脑的品牌,它就会实例化相应品牌的电脑对象

import org.junit.runner.Computer;
public class ComputerFactory {
public static Computer createComputer(String type){
Computer mComputer=null;
switch (type) {
case "lenovo":
mComputer=new LenovoComputer();
break;
case "hp":
mComputer=new HpComputer();
break;
}
return mComputer;
}
}

客户端调⽤⼯⼚类
客户端调⽤⼯⼚类,传⼊“hp”⽣产出惠普电脑并调⽤该电脑对象的start⽅法:

public class CreatComputer {
public static void main(String[]args){
ComputerFactory.createComputer("hp").start();
}
}

Mybatis 体现:

Mybatis中执⾏Sql语句、获取Mappers、管理事务的核⼼接⼝SqlSession的创建过程使⽤到了⼯⼚模 式。

有⼀个 SqlSessionFactory 来负责 SqlSession 的创建

SqlSessionFactory
可以看到,该Factory的openSession ()⽅法重载了很多个,分别⽀
持autoCommit、Executor、Transaction等参数的输⼊,来构建核⼼的SqlSession对象。
在DefaultSqlSessionFactory的默认⼯⼚实现⾥,有⼀个⽅法可以看出⼯⼚怎么产出⼀个产品:

private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level,boolean autoCommit){
Transaction tx=null;
try{
final Environment environment=configuration.getEnvironment();
final TransactionFactory transactionFactory=
getTransactionFactoryFromEnvironment(environment);
tx=transactionFactory.newTransaction(environment.getDataSource(),level,autoCommit);
//根据参数创建制定类型的Executor
final Executor executor=configuration.newExecutor(tx,execType);
//返回的是 DefaultSqlSession
return new DefaultSqlSession(configuration,executor,autoCommit);
}catch(Exception e){
closeTransaction(tx); // may have fetched a connection so lets call
close()
throw ExceptionFactory.wrapException("Error opening session. Cause: "+e,e);
}finally{
ErrorContext.instance().reset();
}
}

这是⼀个openSession调⽤的底层⽅法,该⽅法先从configuration读取对应的环境配置,然后初始化
TransactionFactory 获得⼀个 Transaction 对象,然后通过 Transaction 获取⼀个 Executor 对象,最
后通过configuration、Executor、是否autoCommit三个参数构建了 SqlSession

11.3 代理模式
代理模式(Proxy Pattern):给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式
的英⽂叫做Proxy,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代理
举例:
创建⼀个抽象类,Person接⼝,使其拥有⼀个没有返回值的doSomething⽅法。

/**
* 抽象类⼈
*/
public interface Person {
void doSomething();
}

创建⼀个名为Bob的Person接⼝的实现类,使其实现doSomething⽅法

/**
* 创建⼀个名为Bob的⼈的实现类
*/
public class Bob implements Person { public void doSomething() {
System.out.println("Bob doing something!");
}
}

(3) 创建JDK动态代理类,使其实现InvocationHandler接⼝。拥有⼀个名为target的变量,并创建
getTa rget获取代理对象⽅法

/**
* JDK动态代理
* 需实现 InvocationHandler 接⼝ */
public class JDKDynamicProxy implements InvocationHandler {
//被代理的对象
Person target;
// JDKDynamicProxy 构造函数
public JDKDynamicProxy(Person person) { this.target = person;
}
//获取代理对象
public Person getTarget() { return (Person)
Proxy.newProxylnstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
//动态代理invoke⽅法
public Person invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//被代理⽅法前执⾏
System.out.println("JDKDynamicProxy do something before!");
//执⾏被代理的⽅法
Person result = (Person) method.invoke(target, args);
//被代理⽅法后执⾏
System.out.println("JDKDynamicProxy do something after!"); return
result;
}

创建JDK动态代理测试类J DKDynamicTest

/**
* JDK动态代理测试
*/
public class JDKDynamicTest {
public static void main(String[] args) {
System.out.println("不使⽤代理类,调⽤doSomething⽅法。");
//不使⽤代理类
Person person = new Bob();
// 调⽤ doSomething ⽅法
person.doSomething();
System.out.println("分割线-----------");
System.out.println("使⽤代理类,调⽤doSomething⽅法。");
//获取代理类
Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();
// 调⽤ doSomething ⽅法 proxyPerson.doSomething();
}
}

Mybatis中实现:

代理模式可以认为是Mybatis的核⼼使⽤的模式,正是由于这个模式,我们只需要编写Mapper.java接
⼝,不需要实现,由Mybati s后台帮我们完成具体SQL的执⾏。
当我们使⽤Configuration的getMapper⽅法时,会调⽤mapperRegistry.getMapper⽅法,⽽该⽅法⼜
会调⽤ mapperProxyFactory.newInstance(sqlSession)来⽣成⼀个具体的代理:

public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new
ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T)
Proxy.newProxyInstance(mapperInterface.getClassLoader(), new
Class[] { mapperInterface },
mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession,
mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}

在这⾥,先通过T newInstance(SqlSession sqlSession)⽅法会得到⼀个MapperProxy对象,然后调⽤T
newInstance(MapperProxy mapperProxy)⽣成代理对象然后返回。⽽查看MapperProxy的代码,可
以看到如下内容:

public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

⾮常典型的,该MapperProxy类实现了InvocationHandler接⼝,并且实现了该接⼝的invoke⽅法。通
过这种⽅式,我们只需要编写Mapper.java接⼝类,当真正执⾏⼀个Mapper接⼝的时候,就会转发给
MapperProxy.invoke⽅法,⽽该⽅法则会调⽤后续的
sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回

 

标签:return,String,Builder,代理,模式,学习,mybatis,设计模式,public
From: https://www.cnblogs.com/eagle888/p/16920477.html

相关文章

  • fastapi学习
    Get请求fromfastapiimportFastAPIapp=FastAPI()@app.get("/items/{item_id}")asyncdefread_root(item_id:int):return{"item_id":item_id}#在最......
  • fastapi学习之路
    一、python3写一个http接口服务,给别人调用3这次选择fastapi,FastAPI是一个现代的、快速(高性能)的web框架,用于基于标准Python类型提示使用Python3.6+构建api。具有快速......
  • 深度学习之一致性评价方法
    1.一致性评价方法线性相关系数(Pearson相关)(定量变量)  kappa相关系数(分类数据)  kappa系数(评价者为两人)计算方法:    Fleiss'kappa评价系数(多个评价者存......
  • C++零基础入门学习路线图
    C++入门学习路线图分为三阶段:C++基础入门、C++核心编程、C++提高编程。以下学习路线图参考B站黑马程序员《匠心精作C++从0到1入门编程》C++基础入门 1C++初识 ......
  • Docker学习笔记七:Docker提交镜像到阿里云仓库
    一、准备1、开启阿里云镜像仓库a.访问阿里云地址并登陆地址:http://cr.console.aliyun.comb.选择“容器镜像服务”c.使用“个人实例”构建仓库d.创建命名空......
  • C++全栈开发学习路线图
    C语言基础与提高 C语言基础 指针、内存管理 变量、条件、字符串、数组、函数、结构体 C语言提高 多级指针的使用 接口的封......
  • mysql关于优化设计模式与数据类型
    一、设计数据结构的字段数据类型基本原则是:1、更小的通常更好。就是尽量使用可以正确存储数据的最小数据类型,主要更小的数据类型可以更快,占用磁盘空间,内存和CPU缓存,处理周期......
  • 今日内容 爬虫简单学习
    爬虫介绍#爬虫:spider,网络蜘蛛#本质原理: -现在所有的软件原理:大部分都是基于http请求发送和获取数据的 -pc端的网页-移动端app-模拟发送http......
  • 合宙Air系列开发板官方demo学习(二):camera - 摄像头:(1)-capture
    基础资料基于Air105开发板:Air105-LuatOS文档上手:开发上手-LuatOS文档探讨重点对官方camera-摄像头demo中capture功能的复现,进行相关内容的学习及探讨。实现功......
  • 软件技术基础学习笔记(3)——合作完成一个电梯演讲视频
    软件技术基础https://edu.cnblogs.com/campus/zjlg/22rjjc这个作业的目标<二人合作完成一个电梯演讲>姓名-学号<卢昕豪>-<2020330301200>姓名-学号<王康......