zoukankan      html  css  js  c++  java
  • MyBatis拦截器的执行顺序引发的MyBatis源码分析

    你猜一下哪个先执行?反正不要按常规来。

    1 <plugins>
    2     <plugin interceptor="com.Interceptor1"></plugin>
    3     <plugin interceptor="com.Interceptor2"></plugin>
    4 </plugins>

    之前看有的博客分析源码,都没提到这一点。之前我只是用一下而已,这个顺序测试一下其实结论也很容易获得,但是我有一种看源码的屎命感。MyBatis还算人性化提供了拦截器,iBatis里面就没有了,不过也可以实现。这里要探究拦截器的源码就不得不提到MyBatis的源码,也就是执行流程了。这要是摊开说就有点大了,为了写好这篇,我决定今天晚上回去不打dota了,贡献真实够大的了。

    MyBatis的作用

    名义上来说MyBatis是一个半ORM框架,用了一个半字是因为MyBatis并没有完全起到一个ORM框架的作用(比如Hibernate),还有一半工作是需要我们参与进来——编写SQL语句。MyBatis替我们干的活是啥?帮我们把参数和配置化SQL语句映射成数据库中真正执行的SQL,然后把结果帮我们封装好,并返回回来。好处很容已说明,配置灵活,增强开发人员对SQL语句的控制,减少了冗余的对象封装工作。

    官方的说法如下:

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

    MyBatis的架构

    沿着上面说到的,我们接下来可以看一下MyBatis用了什么架构来完成上面的工作。要注意,talk is cheap,实际上的话还要牵涉很多工作(比如Session,事务等)。在网上看了一些描述MyBatis架构的图,看来一晚上不打dota是不可能了呀。

    功能上的架构:

    源码中的结构:

    架构看上去并不复杂,按三层来分的。之前看的很少,如果你是和我一样的小白话可以一起来看下,从头看起吧,接口和配置文件先开始。

    最简单的方式开始MyBatis

    这里最简单的意思是,我们先抛开Spring,只在一个简单的Maven项目中使用MyBatis,看看它是如何运行的。先起一个简单的Maven项目并加上MyBatis的依赖。

    已经做好了但是,额,写起来估计可以新开一篇了。

    单独使用MyBatis代码

    其实核心代码只有两个,第一个是SessionUtils用于提供Session,第二个是使用Session进行CRUD操作的代码

     1 public class SessionUtils {
     2     private static SqlSessionFactory sessionFactory;
     3     static {
     4         try {
     5             // 使用MyBatis提供的Resources类加载mybatis的配置文件
     6             Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
     7             // 构建sqlSession的工厂
     8             sessionFactory = new SqlSessionFactoryBuilder().build(reader);
     9         } catch (Exception e) {
    10             e.printStackTrace();
    11         }
    12     }
    13 
    14     /**
    15      * 获取SqlSession
    16      * @return SqlSession
    17      */
    18     public static SqlSession getSession() {
    19         return sessionFactory.openSession();
    20     }
    21 }
     1 @Test
     2 public void testInsert() {
     3     SqlSession session = null;
     4     try {
     5         session = SessionUtils.getSession();
     6         StudentMapper studentMapper = session.getMapper(StudentMapper.class);
     7         Long affectedLines = studentMapper.insert(build());
     8         System.out.println("affectedLines = " + (affectedLines == null ? 0 : affectedLines));
     9         session.commit();
    10     } catch (Exception e) {
    11         e.printStackTrace();
    12         if(session != null) {
    13             session.rollback();
    14         }
    15     } finally {
    16         if(session != null) {
    17             session.close();
    18         }
    19     }
    20 }

    SqlSessionFactory的生成

    观察这两段代码引出了两个核心的类:SqlSessionFactorySession

    SqlSessionFactoryBuilder:单纯的就是为了创建SqlSessionFactory,功能很单一。

     1 // 这是一个普类, 而不是接口, 这里把方法都隐去了
     2 public class SqlSessionFactoryBuilder {
     3   public SqlSessionFactory build(Reader reader);
     4   public SqlSessionFactory build(Reader reader, String environment);
     5   public SqlSessionFactory build(Reader reader, Properties properties);
     6   public SqlSessionFactory build(Reader reader, String environment, Properties properties);
     7   public SqlSessionFactory build(InputStream inputStream);
     8   public SqlSessionFactory build(InputStream inputStream, String environment);
     9   public SqlSessionFactory build(InputStream inputStream, Properties properties);
    10   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);
    11   public SqlSessionFactory build(Configuration config);
    12 }

     通过阅读源码发现生成SqlSessionFactory的简要步骤如下:

    通过ReaderXMLConfigBuilderConfigurationDefaultSqlSessionFactory共同协作把SqlSessionFactory弄出来了。如果正式一点,用时序图画出来就是这样的:

     

    承载MySql所有的配置Configuration类

    下面重点关注的类是Configuration。在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration中解析Configuration的代码如下,看完之后你就会觉得很亲切,很多标签都用过:

     1 private void parseConfiguration(XNode root) {
     2   try {
     3     //issue #117 read properties first
     4     propertiesElement(root.evalNode("properties"));
     5     Properties settings = settingsAsProperties(root.evalNode("settings"));
     6     loadCustomVfs(settings);
     7     typeAliasesElement(root.evalNode("typeAliases"));
     8     pluginElement(root.evalNode("plugins"));
     9     objectFactoryElement(root.evalNode("objectFactory"));
    10     objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    11     reflectorFactoryElement(root.evalNode("reflectorFactory"));
    12     settingsElement(settings);
    13     // read it after objectFactory and objectWrapperFactory issue #631
    14     environmentsElement(root.evalNode("environments"));
    15     databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    16     typeHandlerElement(root.evalNode("typeHandlers"));
    17     mapperElement(root.evalNode("mappers"));
    18   } catch (Exception e) {
    19     throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    20   }
    21 }

     话说Java的这个着色也太稀烂了。typeAliases,typeHandlers和mappers是不是很熟悉。然后Configuration的类图是:

    从这个图中可以看出Configuration分为两个部分:

    • 和数据库相关的部分
    • 和文档解析有关的部分

    再返回Configuration的时候,mybatis-config.xml中的各个元素都已经解析出来了:

    Environment:数据源和事务管理器

    Environment就是配置数据源的地方,在mybatis-config.xml中是这样的:

     1 <!-- 配置mybatis运行环境 -->
     2 <environments default="development">
     3     <environment id="development">
     4         <!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
     5         <transactionManager type="JDBC" />
     6         <!-- mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI -->
     7         <!-- POOLED 表示支持JDBC数据源连接池 -->
     8         <!-- UNPOOLED 表示不支持数据源连接池 -->
     9         <!-- JNDI 表示支持外部数据源连接池 -->
    10         <dataSource type="POOLED">
    11             <property name="driver" value="${jdbc.driver}" />
    12             <property name="url" value="${jdbc.url}" />
    13             <property name="username" value="${jdbc.username}" />
    14             <property name="password" value="${jdbc.password}" />
    15         </dataSource>
    16     </environment>
    17 </environments>

     1 private void environmentsElement(XNode context) throws Exception {
     2   if (context != null) {
     3     if (environment == null) {
     4       environment = context.getStringAttribute("default");
     5     }
     6     for (XNode child : context.getChildren()) {
     7       String id = child.getStringAttribute("id");
     8       if (isSpecifiedEnvironment(id)) {
     9         TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
    10         DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
    11         DataSource dataSource = dsFactory.getDataSource();
    12         Environment.Builder environmentBuilder = new Environment.Builder(id)
    13             .transactionFactory(txFactory)
    14             .dataSource(dataSource);
    15         configuration.setEnvironment(environmentBuilder.build());
    16       }
    17     }
    18   }
    19 }

    SqlSessionFactory类图

    session中的主要几个类如下:

    下面则是session相关的类图:

    除了Closeable是java API中的一个接口之外,其他的都是MyBatis中的类或者接口。有好几个类我还不怎么熟悉,比如SqlSession,SqlSessionManager和Executor。但是这里先不管,直接来看session是如何获取的。

  • 相关阅读:
    C++下载IE缓存大文件
    C++网页数据流转为本地文件,URLDownloadToFile
    关于CLISTCTRL的删除和vector的删除,必须倒序删除
    字符串转为 时间。时间差,几天,几小时,几分
    MFCS删除文件夹,非空文件夹
    string的c_str();转换成的是const char*类型 string的c_str();转换成CString,虽然可用,但是意义不一样,变量string不能共用。
    对话框透明,控件不透明(控件显示背景图)
    执行处理程序“System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper”的子请求时出错。
    ffmpeg使用示例
    js图片放大效果
  • 原文地址:https://www.cnblogs.com/tuhooo/p/9188019.html
Copyright © 2011-2022 走看看