Mybatis 运行原理 SqlSessionFactory 的创建

1、通过 SqlSessionFactoryBuilder 类中的静态方法 builder 获取 SqlSessionFactory 对象

public SqlSessionFactory getSqlSessionFactory() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    // 通过 SqlSessionFactoryBuilder 类中的方法 builder 获取 SqlSessionFactory 对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    return sqlSessionFactory;
}

@Test
public void test01() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try{
        EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        Employee emp02 = mapper.getEmpById(1);
        System.out.println(emp02);
        System.out.println(emp01 == emp02);
    } finally {
        sqlSession.close();
    }
}

2、SqlSessionFactoryBuilder 类中的 builder 方法

builder 方法主要做了三件事:

  • 创建 XMLConfigBuilder 对象,该对象主要用于解析 Mybatis 的全局配置文件以及 Mapper 映射文件
  • 调用 XMLConfigBuilder 对象的 parse() 方法,对传入的配置文件进行解析
  • 调用当前 SqlSessionFactoryBuilder 对象的 build 方法,创建 SqlSession 对象,并返回
package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

public class SqlSessionFactoryBuilder {
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 创建 XMLConfigBuilder 对象,主要用于解析 Mybatis 的全局配置文件以及 Mapper 映射文件
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 调用 XMLConfigBuilder 对象的 parse() 方法对传入的配置文件进行解析
      // 调用本类当中的 build 方法,创建 SqlSession 对象,并返回
      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 是 SqlSession 接口的实现类
    return new DefaultSqlSessionFactory(config);
  }

}

3、XMLConfigBuilder 类中的 parse() 方法

XMLConfigBuilder 继承了 BaseBuilder,Configuration 变量是在 BaseBuilder 中声明的。在 XMLConfigBuilder 类中,其构造函数在初始时便调用了父类的构造函数,并传入了 Configuration 对象。

parse() 方法主要用于解析 Mybatis 全局配置文件以及 Mapper 映射文件中的内容,并将其中的相关内容封装到 Configuration 对象中

BaseBuilder.java

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

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
}

XMLConfigBuilder.java

public class XMLConfigBuilder extends BaseBuilder {

  private boolean parsed;
  private final XPathParser parser;
  private String environment;
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
  
  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;
  }

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  
  // 该方法主要用于解析 Mybatis 全局配置文件以及 Mapper 映射文件中的内容,并将其中的相关内容封装到 Configuration 对象中
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // 解析 properties 标签:引入外部properties文件
      propertiesElement(root.evalNode("properties"));
      // 解析 settings 标签
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      // 解析 typeAliases 标签
      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"));
      // 解析 Mapper 映射文件
      // 重点关注此方法
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  
  // 判断全局配置文件中是如何配置映射文件相关信息的
  // 案例中使用的 package 标签,其他条件不再关注
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      	// 如果是以 package 标签进行配置的
        if ("package".equals(child.getName())) {
          // 获取 package 标签中的 name 属性的值
          String mapperPackage = child.getStringAttribute("name");
          // 调用 Configuration 对象的 addMappers 方法
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
}

4、Configuration 类中的 addMappers 方法

public class Configuration {
   protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

   public void addMappers(String packageName) {
   	 // 调用 MapperRegistry 对象的 addMappers 方法
     mapperRegistry.addMappers(packageName);
  }
}

5、MapperRegistry 类中的 addMappers 方法

addMappers 方法创建了 Mapper 接口对应的代理对象工厂,后续会由代理对象工厂来创建代理对象,并以 Mapper 接口为 key,代理对象工厂为 value,封装进 knowMappers(Map集合)

public class MapperRegistry {

  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  
  public <T> void addMapper(Class<T> type) {
  	// 判断传入的 Class 是否是接口
    if (type.isInterface()) {
      // 如果 knownMappers 中已经包含当前 Class
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
      	// 创建接口对应的代理对象工厂,并以 Mapper 接口为 key,代理对象工厂为 value,封装进 knowMappers(Map集合)
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        // 创建 MapperAnnotationBuilder 对象,并调用 parse() 方法
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }
  
 public void addMappers(String packageName, Class<?> superType) {
 	// 创建 ResolverUtil 工具类
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    // 根据 packageName,即 mapper 文件所在的包路径,将 mapper 文件对应的 Mapper 接口全类名存放到一个 set 集合中
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    // 获取 Mapper 接口的 set 集合
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 遍历 Mapper 接口,并调用 addMapper 方法,该方法主要用于创建 Mapper 接口对应的代理对象
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

  /**
   * @since 3.2.2
   */
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }
}

6、MapperAnnotationBuilder 中的 parse() 方法

public class MapperAnnotationBuilder {

  private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
  private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();

  private final Configuration configuration;
  private final MapperBuilderAssistant assistant;
  private final Class<?> type;

  public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;

    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);

    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }

  public void parse() {
  	// 获取 Mapper 接口的全类名
    String resource = type.toString();
    // 判断 Configuration 对象中是否已经加载过该资源
    if (!configuration.isResourceLoaded(resource)) {
      // 加载 Mapper 接口对应的 xml 文件
      loadXmlResource();
      // 将 Mapper 接口全类名存放进名为 loadedResources 的 set 集合
      configuration.addLoadedResource(resource);
      // 设置当前 Mapper.xml 的 namespace
      assistant.setCurrentNamespace(type.getName());
      // 解析缓存
      parseCache();
      // 解析引用缓存
      parseCacheRef();
      // 获取 Mapper 接口中所有的方法信息
      Method[] methods = type.getMethods();
      // 遍历方法
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
          	// 调用 parseStatement 方法
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

  void parseStatement(Method method) {
  	// 获取方法的参数类型
    Class<?> parameterTypeClass = getParameterType(method);
    LanguageDriver languageDriver = getLanguageDriver(method);
    // 获取 mapper.xml 文件中的 SQL 语句块信息
    // sqlSource 对象包含三个变量,一是 SQL 语句,二是参数映射信息,三是 Configuration 对象
    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
    if (sqlSource != null) {
      Options options = method.getAnnotation(Options.class);
      final String mappedStatementId = type.getName() + "." + method.getName();
      Integer fetchSize = null;
      Integer timeout = null;
      StatementType statementType = StatementType.PREPARED;
      ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
      // 获取 SQL 命令类型(select、update、insert、delete)
      SqlCommandType sqlCommandType = getSqlCommandType(method);
      // 是否是查询命令
      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      // 是否刷新缓存
      boolean flushCache = !isSelect;
      // 是否使用缓存
      boolean useCache = isSelect;

      KeyGenerator keyGenerator;
      String keyProperty = "id";
      String keyColumn = null;
      // 如果是insert或者update命令
      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // first check for SelectKey annotation - that overrides everything else
        SelectKey selectKey = method.getAnnotation(SelectKey.class);
        if (selectKey != null) {
          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
          keyProperty = selectKey.keyProperty();
        } else if (options == null) {
          keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        } else {
          keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          keyProperty = options.keyProperty();
          keyColumn = options.keyColumn();
        }
      } else {
        keyGenerator = NoKeyGenerator.INSTANCE;
      }

      if (options != null) {
        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
          flushCache = true;
        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
          flushCache = false;
        }
        useCache = options.useCache();
        fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
        timeout = options.timeout() > -1 ? options.timeout() : null;
        statementType = options.statementType();
        resultSetType = options.resultSetType();
      }

      String resultMapId = null;
      ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
      if (resultMapAnnotation != null) {
        String[] resultMaps = resultMapAnnotation.value();
        StringBuilder sb = new StringBuilder();
        for (String resultMap : resultMaps) {
          if (sb.length() > 0) {
            sb.append(",");
          }
          sb.append(resultMap);
        }
        resultMapId = sb.toString();
      } else if (isSelect) {
        resultMapId = parseResultMap(method);
      }
	 // 将 MappedStatement 封装进 Configuration 对象中
      assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          // ParameterMapID
          null,
          parameterTypeClass,
          resultMapId,
          getReturnType(method),
          resultSetType,
          flushCache,
          useCache,
          // TODO gcode issue #577
          false,
          keyGenerator,
          keyProperty,
          keyColumn,
          // DatabaseID
          null,
          languageDriver,
          // ResultSets
          options != null ? nullOrEmpty(options.resultSets()) : null);
    }
  }
  
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值