Mybatis源码解析-SqlSessionFactory

传统的写法JDBC

        String URL = "jdbc:mysql://127.0.0.1:3306/student?serverTimezone = GMT";
        String USER = "root";
        String PASSWORD = "123456";
        // 1.加载驱动程序
        try {
            Class.forName("com.mysql.jdbc.Driver");
            // 2.获得数据库链接
            Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
            // 3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
            //预编译
            String sql="insert into studentTable (id,name,age) values (?,?,?)";
            PreparedStatement statement = conn.prepareStatement(sql);
            statement.setInt(1, 1);
            statement.setString(2, "张三");
            statement.setInt(3, 22);
            int i = statement.executeUpdate();
//			String sql="select * from studentTable where UserName='"+name+"'";
//			Statement statement = conn.createStatement();
//			ResultSet rs = statement.executeQuery(sql);
            System.out.println("插入条数:"+i);

            // 关闭资源
            conn.close();
            statement.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }

源码SqlSessionFactory的创建

我们从JDBC执行一条sql的过程来分析mybatis是怎么从加载配置文件,比如:全局配置文件、XxxMapper.xml配置文件来执行sql的

String resource = "config/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentTable s=new StudentTable();
s.setId(1);
s.setAge("22");
s.setName("张三");
sqlSession.insert("insertStudent",s);
sqlSession.commit();
sqlSession.close();

首先根据mybatis-config.xml配置文件路径创建输入流,将输入流对象作为参数传到SqlSessionFactoryBuilder中的build方法得到一个SqlSessionFactory对象,源码如下

public class SqlSessionFactoryBuilder {
//省略其他代码
public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //解析得到一个XMLConfigBuilder对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment,properties);
      //将Configuration对象传给DefaultSqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
  	//最后默认返回DefaultSqlSessionFactory
    return new DefaultSqlSessionFactory(config);
  }
}

SqlSessionFactory是通过SqlSessionFactoryBuilder工厂类的build方法创建的, 不是通过构造方法直接new出来的。
此外,我们还可以知道,SqlSessionFactory提供了字节流和字符流以及直接的org.apache.ibatis.session.Configuration配置类的三种方式来读取配置文件信息。
但是无论是字符流还是字节流,然后将Configuration设置到SqlSessionFactory(默认的实现类是org.apache.ibatis.session.defaults.DefaultSqlSessionFactory)中的configuraion字段并且返回。
所以其本身也是很简单的,解析xml配置文件的操作都委托给了XMLConfigBuilder类。
接着点开XMLConfigBuilder类看看是怎么创建XMLConfigBuilder对象的,可以看到XMLConfigBuilder 是继承BaseBuilder 建造者对象,看看BaseBuilder 有哪些子类
1.0
点开XMLConfigBuilder,XMLConfigBuilder类的主要代码如下:
XMLConfigBuilder以及解析Mapper.xml文件的XMLMapperBuilder都继承自BaseBuilder。
他们对xml文件的加载和解析都交给了XPathParser,从而最终使用JDK自带的xml解析器而非dom4j、jdom第三方jar包,底层使用xPath进行节点解析。new XPathParser(reader, true, props, new XMLMapperEntityResolver()) 四个参数的含义分别是:配置文件流、是否进行DTD校验、属性配置、xml实体节点解析器。EntityResolver比较好理解,跟Spring的XML标签解析器一样,有默认的解析器,也有自定义的比如tx,dubbo等,主要使用了策略模式,在这里mybatis硬编码为了XMLMapperEntityResolver

public class XMLConfigBuilder extends BaseBuilder {
	//省略其他代码
	public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
	    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
	  }
	
	private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	    super(new Configuration());
	    ErrorContext.instance().resource("SQL Mapper Configuration");
	    this.configuration.setVariables(props);
	    this.parsed = false;
	    this.environment = environment;
	    this.parser = parser;
	  }
}

XPathParser类代码如下:
补充一下:EntityResolver的作用:EntityResolver的实现类XMLEntityResolver类中引用了本地的DTD文件,和本类在同一个package下,其中的ibatis-3-config.dtd应该主要是用于兼容用途。
其次就是XPathParser做了以下几件事:
1.将是否验证dtd(DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。),节点解析器进行设置,并且初始化xPath,便于xml文件解析和加载。
2.可知,commonConstructor并没有做什么。回过头到createDocument上,其使用了org.xml.sax.InputSource作为参数。代码实现如下:

public class XPathParser {

  private final Document document;
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;

  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
  	// 1.将是否验证dtd,节点解析器进行设置,并且初始化xPath,便于xml文件解析和加载
    commonConstructor(validation, variables, entityResolver);
    // 2.可知,commonConstructor并没有做什么。回过头到createDocument上,其使用了org.xml.sax.InputSource作为参数,createDocument的关键代码如下:
    this.document = createDocument(new InputSource(inputStream));
  }

// 创建document文档
private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);
	 // 设置有本工厂创建的解析器文件是否支持xml命名空间
      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      // 是否忽略元素配置中的空格
      factory.setIgnoringElementContentWhitespace(false);
      // 设置是否将CDATA节点转换为Text节点
      factory.setCoalescing(false);
      // 设置是否展开实体节点,关系到sql片段的相关配置
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      // 设置解析mybatis-config.xml文档节点的解析器,也就是上面的XmlMapperEntityResolver
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      //主要是根据mybatis自身需要创建一个=文档解析器,调用parse方法(inputstream int).将解析后的xml以document对象的形式返回
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }
  //主要进行一些配置 
  private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }
	// ···省略其他无关代码···
}

接下来可以看 XMLConfigBuilder对象的创建是怎样的。

public class XMLConfigBuilder extends BaseBuilder {
	private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	    super(new Configuration());
	    ErrorContext.instance().resource("SQL Mapper Configuration");
	    this.configuration.setVariables(props);
	    this.parsed = false;
	    this.environment = environment;
	    this.parser = parser;
  }
}

可以看出:主要调用了父类BaseBuilder的构造方法(作用:进行类型别名处理器注册、类型处理器注册)

public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    // Configuration类的初始化的做了一些事情,后面讲具体是什么事。
    this.configuration = configuration;
    // 别名处理器
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    // 类型处理器
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
}

之后,设置environment及props等核心配置信息,(Configuration类是MyBatis的核心配置类,十分重要)。XmlConfigBuilder创建完毕之后,代码会回到SqlSessionFactoryBuilder类build(Reader reader, String environment, Properties properties)方法中,然后调用parser.parse()方法。至此,第一阶段:加载xml配置文件并转为Document对象完毕,而所有的配置过程都在parse()这个方法中(也是对Configuration类的配置)。

public class XMLConfigBuilder extends BaseBuilder {
	//省略其他代码
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //通过parseConfiguration(parser.evalNode("/configuration"))得知,xml文件中的configuration中的内容已经全部加载到XNode中了,后续的相关配置数据的获取都是通过这个XNode来获取的。
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
}

Configuration类的配置文件这片文章会介绍,暂时还没写
链接: https://blog.csdn.net/qq_40913932/article/details/112840913
我们来看一下整个配置文件解析流程
1.1
借鉴于
链接: https://blog.csdn.net/weixin_39723544/article/details/84028645
https://mp.weixin.qq.com/s/3_Fh8hTnMHMWX1e1EGe5-w

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一款开源的持久层框架,它支持自定义SQL、存储过程和高级映射。本文将深度解析MyBatis源码,包括其核心组件和实现原理。 1. MyBatis的核心组件 MyBatis的核心组件包括: - SqlSessionFactoryBuilder:用于创建SqlSessionFactory,它会读取配置文件并创建相应的对象。 - SqlSessionFactory:用于创建SqlSession,它是线程安全的,因此可以被共享。 - SqlSession:用于执行SQL语句,它是非线程安全的,因此每个线程都需要创建自己的SqlSession。 - Executor:用于执行SQL语句。 - StatementHandler:用于处理SQL语句的处理器。 - ParameterHandler:用于处理参数的处理器。 - ResultSetHandler:用于处理结果集的处理器。 - MappedStatement:映射配置信息,包括SQL语句、参数类型、返回值类型等。 - Configuration:MyBatis的配置信息,包括数据源、类型转换器、映射配置等。 2. MyBatis的实现原理 MyBatis的实现原理主要包括以下几个方面: 2.1 映射配置 MyBatis使用XML或注解的方式进行映射配置,其配置信息包括: - 映射文件的命名空间。 - 映射文件中定义的SQL语句,包括select、insert、update、delete等。 - SQL语句的参数类型和返回值类型。 - SQL语句的参数映射关系,包括#{paramName}形式的占位符和${paramName}形式的变量。 - SQL语句的结果映射关系,包括<resultMap>标签和<result>标签。 2.2 解析配置文件 MyBatis会通过SqlSessionFactoryBuilder读取配置文件,并将其解析成一个Configuration对象。解析过程中会进行数据源的配置、类型转换器的配置、映射配置的加载等。 2.3 创建SqlSessionFactory MyBatis解析配置文件之后,会根据配置信息创建SqlSessionFactory对象。SqlSessionFactory是线程安全的,因此可以被共享。它主要负责创建SqlSession对象。 2.4 创建SqlSession 每个SqlSession都会绑定一个Connection对象,因此它是非线程安全的。MyBatis会为每个线程创建自己的SqlSession对象。SqlSession主要负责执行SQL语句。 2.5 执行SQL语句 MyBatis在执行SQL语句时,会先根据SQL语句的ID从MappedStatement中获取相应的映射信息,然后使用Executor执行SQL语句。在执行SQL语句时,会使用StatementHandler处理SQL语句,ParameterHandler处理参数,ResultSetHandler处理结果集。 3. 总结 本文深度解析MyBatis源码,包括其核心组件和实现原理。MyBatis是一个功能强大的持久层框架,可以帮助我们简化数据库操作。同时,MyBatis源码也值得我们深入学习和研究。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值