广告
返回顶部
首页 > 资讯 > 后端开发 > Python >分析mybatis运行原理
  • 348
分享到

分析mybatis运行原理

2024-04-02 19:04:59 348人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录一、mybatis基本认识1.1、动态代理1.2、反射二、Configuration对象作用三、映射器结构四、sqlsession执行流程(源码跟踪)4.1、Executor4.

一、Mybatis基本认识

1.1、动态代理

  • 之前我们知道Mapper仅仅是一个接口,而不是一个逻辑实现类。但是在Java中接口是无法执行逻辑的。这里Mybatis就是通过动态代理实现的。关于动态代理我们常用的有jdk动态代理和cglib动态代理。两种却别这里不做赘述。关于CGLIB代理在框架中使用的比较多。
  • 关于动态代理就是所有的请求有一个入口,由这个入口进行分发。在开发领域的一个用途就是【负载均衡
  • 关于Mybatis的动态代理是使用了两种的结合。
  • 下面看看JDK和cglib两种实现

JDK实现首先我们需要提供一个接口 , 这个接口是对我们程序员的一个抽象。 拥有编码和改BUG的本领


public interface Developer {

    
    void code();

    
    void debug();
}

关于这两种本领每个人处理方式不同。这里我们需要一个具体的实例对象


public class JavaDeveloper implements Developer {
    @Override
    public void code() {
        System.out.println("java code");
    }

    @Override
    public void debug() {
        System.out.println("java debug");
    }
}

我们传统的调用方式是通过java提供的new 机制创造一个JavaDeveloper对象出来。而通过动态代理是通过java.lang.reflect.Proxy对象创建对象调用实际方法的。

通过newProxyInstance方法获取接口对象的。而这个方法需要三个参数

  • ClassLoader loader : 通过实际接口实例对象获取ClassLoader
  • Class<?>[] interfaces : 我们抽象的接口
  • InvocationHandler h : 对我们接口对象方法的调用。在调用节点我们可以进行我们的业务拦截

JavaDeveloper jDeveloper = new JavaDeveloper();
Developer developer = (Developer) Proxy.newProxyInstance(jDeveloper.getClass().getClassLoader(), jDeveloper.getClass().getInterfaces(), (proxy, method, params) -> {
    if (method.getName().equals("code")) {
        System.out.println("我是一个特殊的人,code之前先分析问题");
        return method.invoke(jDeveloper, params);
    }
    if (method.getName().equals("debug")) {
        System.out.println("我没有bug");

    }
    return null;
});
developer.code();
developer.debug();

CGLIB动态代理

cglib动态代理优点在于他不需要我们提前准备接口。他代理的实际的对象。这对于我们开发来说就很方便了。


public class HelloService {
    public HelloService() {
        System.out.println("HelloService构造");
    }

    final public String sayHello(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }

    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

下面我们只需要实现cglib提供的MethodInterceptor接口,在初始化设置cglib的时候加载这个实例化对象就可以了


public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}

下面我们就来初始化设置cglib


public static void main(String[] args) {
    //代理类class文件存入本地磁盘方便我们反编译查看源代码
    System.setProperty(DebugginGClassWriter.DEBUG_LOCATION_PROPERTY, "/root/code");
    //通过CGLIB动态代理获取代理对象过程
    Enhancer enhancer = new Enhancer();
    //设置enhancer对象的父类
    enhancer.setSuperclass(HelloService.class);
    // 设置enhancer的回调对象
    enhancer.setCallback(new MyMethodInterceptor());
    //创建代理对象
    HelloService helloService = (HelloService) enhancer.create();
    //通过代理对象调用目标方法
    helloService.sayHello();
}

仔细看看cglib和springaop特别像。针对切点进行切面拦截控制。

总结:

通过对比两种动态代理我们很容易发现,mybatis就是通过JDK代理实现Mapper调用的。我们Mapper接口实现通过代理到xml中对应的sql执行逻辑

1.2、反射

  • 相信有一定经验的Java工程师都对反射或多或少有一定了解。其实从思想上看不惯哪种语言都是有反射的机制的。
  • 通过反射我们就摆脱了对象的限制我们调用方法不再需要通过对象调用了。可以通过Class对象获取方法对象。从而通过invoke方法进行方法的调用了。

二、Configuration对象作用

Configuration对象存储了所有Mybatis的配置。主要初始化一下参数

  • properties
  • settings
  • typeAliases
  • typeHandler
  • ObjectFactory
  • plugins
  • environment
  • DatabaseIdProvider
  • Mapper映射器

三、映射器结构

  • BoundSql提供三个主要的属性 parameterMappings 、parameterObject、sql
  • parameterObject参数本身。我们可以传递java基本类型、POJO、Map或者@Param标注的参数。
  • 当我们传递的是java基本类型mybatis会转换成对应的包装对象 int -> Integer
  • 如果我们传递POJO、Map。就是对象本身
  • 我们传递多个参数且没有@Param指定变量名则parameterObject 类似
  • {"1":p1,"2":p2,"param1":p1,"param2":p2}
  • 我们传递多个参数且@Param指定变量名 则parameterObject类似
  • {"key1":p1,"key2":p2,"param1":p1,"param2":p2}
  • parameterMapping 是记录属性、名称、表达式、javaType,jdbcType、typeHandler这些信息
  • sql 属性就是我们映射器中的一条sql. 正常我们在常见中对sql进行校验。正常不需要修改sql。

四、sqlsession执行流程(源码跟踪)

首先我们看看我们平时开发的Mapper接口是如何动态代理的。这就需要提到MapperProxyFactory这个类了。该类中的newInstance方法


protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

通过上满代码及上述对jdk动态代理的表述。我们可以知道mapperProxy是我们代理的重点。MapperProxy是InvocationHandler的实现类。他重写的invoke方法就是代理对象执行的方法入口。


@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);
}

private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
    & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
    && method.getDeclaringClass().isInterface();
}

通过源码发现。invoke内部首先判断对象是否是类 。 通过打断点发现最终会走到cacheMapperMethod这个方法去创建MapperMethod对象。继续查看MapperMethod中execute方法我们可以了解到内部实现其实是一个命令行模式开发。通过判断命令从而执行不同的语句。判断到具体执行语句然后将参数传递给sqlsession进行sql调用并获取结果。到了sqlsession就和正常jdbc开发sql进行关联了。sqlsession中ExecutorStatementHandlerParameterHandlerResulthandler四大天王

4.1、Executor

顾名思义他就是一个执行器。将java提供的sql提交到数据库。Mybatis提供了三种执行器。

Configuration.classnewExecutor源码

根据uml我们不难看出mybatis中提供了三类执行器分别SimpleExecutor、ReuseExecutor、BatchExecutor


public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 得到configuration 中的environment
      final Environment environment = configuration.getEnvironment();
      // 得到configuration 中的事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 获取执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回默认的SqlSession
      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();
    }
  }

通过上述源码我们知道在sqlsession获取一个数据库session对象时我们或根据我们的settings配置加载一个Executor对象。在settings中配置也很简单


<settings>
<!--取值范围 SIMPLE, REUSE, BATCH -->
	<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

我们也可以通过java代码设置


factory.openSession(ExecutorType.BATCH);

4.2、StatementHandler

顾名思义,StatementHandler就是专门处理数据库回话的。这个对象的创建还是在Configuration中管理的。


public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

很明显Mybatis中StatementHandler使用的是RoutingStatementHandler这个class

关于StatementHandler和RoutingStatementHandler之间的关系我们通过源码可以看出这里和Executor一样都是适配器模式。采用这种模式的好处是方便我们对这些对象进行代理。这里读者可以猜测一下是使用了哪种动态代理。给点提示 这里使用了接口哦


在查看BaseStatementHandler结构我们会发现和Executor一模一样。同样的Mybatis在构造RoutingStatementHandler的时候会根据setting中配置来加载不同的具体子类。这些子类都是继承了BaseStatementHandler.

前一节我们跟踪了Executor。 我们知道Mybatis默认的是SimpleExecutor。 StatementHandler我们跟踪了Mybaits默认的是PrePareStatementHandler。在SimpleExecutor执行查询的源码如下


我们发现在executor查询钱会先让statementHandler构建一个Statement对象。最终就是StatementHandler中prepare方法。这个方法在抽象类BaseStatmentHandler中已经封装好了。

这个方法的逻辑是初始化statement和设置连接超时等一些辅助作用

然后就是设置一些参数等设置。最后就走到了执行器executor的doquery

PrepareStatement在我们jdbc开发时是常见的一个类 。 这个方法执行execute前我们需要设置sql语句,设置参数进行编译。这一系列步骤就是刚才我们说的流程也是PrepareStatementHandler.prepareStatement帮我们做的事情。那么剩下的我们也很容易想到就是我们对数据结果的封装。正如代码所示下马就是resultSetHandler帮我们做事情了。

4.3、结果处理器(ResultSetHandler)


@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapid = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

这个方法我们可以导出来是结果xml中标签配置对结果的一个封装。

4.4、总结

SqlSession在一个查询开启的时候会先通过CacheExecutor查询缓存。击穿缓存后会通过BaseExector子类的SimpleExecutor创建StatementHandler。PrepareStatementHandler会基于PrepareStament执行数据库操作。并针对返回结果通过ResultSetHandler返回结果数据

以上就是分析mybatis运行原理的详细内容,更多关于mybatis运行原理的资料请关注编程网其它相关文章!

--结束END--

本文标题: 分析mybatis运行原理

本文链接: https://www.lsjlt.com/news/128547.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 分析mybatis运行原理
    目录一、Mybatis基本认识1.1、动态代理1.2、反射二、Configuration对象作用三、映射器结构四、sqlsession执行流程(源码跟踪)4.1、Executor4....
    99+
    2022-11-12
  • Mybatis 连接mysql数据库底层运行的原理分析
    目录什么是mybatis首先拆解mybatis架构我将mybatis主要拆分成三个部分在mybatis官网上找的入门代码第一点、数据源的获取第二点、获取执行语句第三点、操作数据源工作...
    99+
    2022-11-13
  • JavaScript运行原理的示例分析
    这篇文章主要介绍JavaScript运行原理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!JavaScript是一种基于对象的动态、弱类型脚本语言(以下简称JS),是一种解...
    99+
    2022-10-19
  • SparkSQl中运行原理的示例分析
    这篇文章将为大家详细讲解有关SparkSQl中运行原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一:什么是SparkSQL?(一)SparkSQL简介Spark SQL是Spark的一个模块...
    99+
    2023-06-20
  • 如何进行Java Mybatis中的Mapper原理分析
    如何进行Java Mybatis中的Mapper原理分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。准备1.pom文件 <depen...
    99+
    2023-06-26
  • 原理分析Java Mybatis中的Mapper
    目录准备1.pom文件2.user类-数据库3.实体类4.dao 层5.Mapper 文件源码分析1.断点2.查看源码总结准备 1.pom文件 <dependencies&g...
    99+
    2022-11-12
  • Mybatis-Plus注入SQL原理分析
    目录前言案例测试原理解析前言 MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 那么 MyBat...
    99+
    2022-11-13
  • Flink on yarn运行原理的示例分析
    小编给大家分享一下Flink on yarn运行原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!Flink运行时由两种类型的进程组成:1),JobManager也叫master协调分布式执行。他们调度任务,协调...
    99+
    2023-06-19
  • Mybatis省略@Param注解原理分析
    目录1、新建mybatis的Demo项目2、添加-parameters参数后的执行结果如下3、springboot项目为什么不用另外配置-parameters参数呢环境配置: jdk...
    99+
    2022-11-13
  • Mybatis防止sql注入原理分析
    目录Mybatis防止sql注入原理底层实现原理Mybatis解决sql注入问题小结一下Mybatis防止sql注入原理 SQL 注入是一种代码注入技术,用于攻击数据驱动的应用,恶意...
    99+
    2022-11-12
  • Mybatis的Dao层实现原理分析
    目录1.Mybatis的Dao层实现1.1 传统开发方式1.2 代理开发方式1.3 知识小结2.MyBatis映射文件深入2.1 动态sql语句2.2 SQL片段抽取2.3 知识小结...
    99+
    2022-12-21
    Mybatis的Dao层 Dao层实现原理 Mybatis Dao层原理
  • MyBatis工作原理的示例分析
    这篇文章给大家分享的是有关MyBatis工作原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、Mybatis工作原理Mybatis分层框架图Mybatis工作原理图源码分析:一般都是从hellowo...
    99+
    2023-06-15
  • mybatis-plugin插件执行原理解析
    mybatis-plugin插件执行原理 今天主要是在看mybatis的主流程源码,其中比较感兴趣的是mybatis的plugin功能,这里主要记录下mybatis-plugin的插...
    99+
    2022-11-13
    mybatis-plugin插件 mybatis-plugin插件原理
  • SpringBoot深入分析运行原理与功能实现
    目录前言pom.xml文件分析启动器starter启动引导类内置的服务器内嵌Tomcat定义位置tomcat运行原理修改服务器添加服务器更换内嵌服务器前言 我们从以下几个方面研究: ...
    99+
    2022-11-13
  • 最全面的SpringBoot教程(六)——SpringBoot运行原理分析
    前言 本文为 最全面的SpringBoot教程(六)——SpringBoot运行原理分析 相关知识,下边将对SpringBoot运行原理以及自动配置原理进行详尽的分析介绍~ 📌博主主...
    99+
    2023-09-14
    spring boot java spring
  • MyBatis接口的简单实现原理分析
    用过MyBatis3的人可能会觉得为什么MyBatis的Mapper接口没有实现类,但是可以直接用?那是因为MyBatis使用Java动态代理实现的接口。这里仅仅举个简单例子来说明原理,不是完全针对MyBatis的,这种思想我们也可以应用在...
    99+
    2023-05-31
    mybatis 接口 原理
  • Mybatis 插件原理解析
    Mybati s作为⼀个应⽤⼴泛的优秀的ORM开源框架,这个框架具有强⼤的灵活性,在四⼤组件 (Executor...
    99+
    2022-11-12
  • spring-mybatis与原生mybatis使用的示例分析
    小编给大家分享一下spring-mybatis与原生mybatis使用的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!原生mybatis使用方法:String resource = &...
    99+
    2023-05-30
    spring mybatis
  • MyBatis框架底层的执行原理源码解析
    目录1.前言2.案例项目源码3.MyBatis源码解析底层执行原理3.1 读取mybatis配置文件创建出SqlSeesionFactory对象3.2 通过SqlSeesionFac...
    99+
    2022-11-13
  • 解析Mybatis SqlSessionFactory初始化原理
    目录引言SqlSessionFactory不使用 XML 构建 SqlSessionFactorySqlSessionFactoryBuilder拓展引言 现在内卷越来越严重,关于...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作