Mybatis源码分析

Mybatis源码分析

一,mybatis执行代码

        //读取配置文件
        InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");

        
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

        //获取SqlSessionFactory 
        SqlSessionFactory sessionFactory = builder.build(is);

        //获取SqlSession
        SqlSession openSession = sessionFactory.openSession();
        //获取代理对象
        UserDao userDao = openSession.getMapper(UserDao.class);
        User user=userDao.findById(1);

        //资源关闭
        openSession.close();
        is.close();

二,源码分析

1.SqlSessionFactory sessionFactory = builder.build(is)方法执行流程,这个方法做了些什么操作?

源码:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //使用XMLConfigBuilder来解析mybatis全局的配置文件
      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.
      }
    }
  }
1.1.先看看这个类的创建XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

源码

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //调用父类构造器初始化一个Configuration
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    //parsed默认为为fasle
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
1.2.parser.parse()

源码

 public Configuration parse() {
    //parsed默认为false
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //下面将parsed设为true,表示XMLConfigBuilder parseConfiguration()方法只能执行一次,真正解析操作解析一次
    parsed = true;
    //解析配置文件
    parseConfiguration(parser.evalNode("/configuration"));
    //返回一个Configuration类的对象,里面 分装好了mybatis的配置文件信息
    return configuration;
  }
1.2.1parseConfiguration(parser.evalNode("/configuration"));

源码

//对mybatis配置文件中的各个标签进行解析,将xml转换成javaBean的过程,并存储在全局的容器 Configuration中
private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(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"));
      //解析mappers标签 会将Mapper.xml 也解析完加入到 Configuration中
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
补充:root.evalNode("/configuration"),这个方法是找出configuration标签里所有的一级子标签
XNode类:
public List<XNode> evalNodes(String expression) {
    return xpathParser.evalNodes(node, expression);
  }

XPathParser类:
public List<XNode> evalNodes(String expression) {
    return evalNodes(document, expression);
  }

  public List<XNode> evalNodes(Object root, String expression) {
    List<XNode> xnodes = new ArrayList<XNode>();
    NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++) {
      xnodes.add(new XNode(this, nodes.item(i), variables));
    }
    return xnodes;
  }

1.2.1.1标签解析environmentsElement(root.evalNode(“environments”)); 其标签可以自己去看看

这里 就 不一一列出了

源码

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
        //遍历子标签去解析 
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
            
          //会将解析完标签后的数据存入Configuration 的对象中
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

上面执行完了build(parser.parse()); 里的parser.parse() 方法,解析了配置文件,及*Mapper.xml文件

下面会具体对解析*Mapper.xml文件进行源码解读,解析的内容都存储在全局容器Configuration中并返回

1.3:buile() 创建SqlSessionFactory

源码

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  
  
   public SqlSessionFactory build(Configuration config) {
   //根据Configuration返回一个默认的 DefaultSqlSessionFactory
    return new DefaultSqlSessionFactory(config);
  }

上述步骤SqlSessionFactory 已经创建好了mybatis配置 文件解析完存储在Configuration中了,把xml转成javaBean对象

2.SqlSession openSession = sessionFactory.openSession();

源码

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

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        //获取Configuration中的Environment,这些Environment(环境信息)都是包括使用哪种数据库,连接数据库的信息,事务 
      final Environment environment = configuration.getEnvironment();
      //根据环境信息关于事务的配置获取事务工厂    
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //environment.getDataSource() 根据环境信息关于数据库的配置获取数据源,添加到事务中
        //level 事务隔离级别;autoCommit 是否自动提交 在openSession()中可以传入这个参数值
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //从configuration中获取一个执行器实例    
      final Executor executor = configuration.newExecutor(tx, execType);
      //根据configuration,executor,autoCommit创建一个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(); 
    }
  }
//DefaultSqlSession构造器方法 dirty默认设为false
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

sessionFactory.openSession()创建了事务管理工厂,创建了Executor

public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}
三种执行器: SIMPLE 
           BATCH 
           REUSE 

上述俩个方法的操作

步骤一:从主配置文件流中读取文件的各个节点信息并存放到Configuration对象中。读取mappers节点的引用文件,并将这些文件的各个节点信息存放到Configuration对象

步骤二:根据Configuration对象的信息获取数据库连接,并设置连接的事务隔离级别等信息,将经过包装数据库连接对象SqlSession接口返回,DefaultSqlSession是SqlSession的实现类,所以这里返回的是DefaultSqlSession,SqlSession接口里面就是对外提供的各种数据库操作。

3.获取代理对象 UserDao userDao = openSession.getMapper(UserDao.class);

源码

public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //new 一个代理对象创建工厂
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //创建代理对象返回  
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

3.1mapperProxyFactory.newInstance(sqlSession);

源码

 public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    //创建好了代理对象返回 根据sqlSession:DefaultSqlSession  mapperInterface:UserDao methodCache:方法缓存
    return newInstance(mapperProxy);
  }
  
   protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }
  
 

4.User user=userDao.findById(1); 方法执行流程

源码 :代理对象invoke 方法的执行

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    //判断是不是Object的方法
      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);
  }
4.1.mapperMethod.execute(sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    
    //参数转换与SQL进行绑定,执行SQL
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          //SQL执行
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
//解析转换获取参数
public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }
4.2sqlSession.selectOne(command.getName(), param);

源码

 public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //方法执行
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      //结果返回
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
  
  //方法执行跳转
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
  
  //方法执行
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //MappedStatement 它存储了一个 sql 对应的所有信息
      MappedStatement ms = configuration.getMappedStatement(statement);
      //用方法执行器来执行
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

至此方法执行发,mybatis运行源码流程

5.仔细分析一下**Mapper.xml文件 的解析过程mapperElement(root.evalNode(“mappers”));

源码

private void mapperElement(XNode parent) throws Exception {
		if (parent != null) {
			// 开始遍历每1个节点
			for (XNode child : parent.getChildren()) {
			/*
			<mappers>
        <mapper resource=""></mapper>
        <mapper url=""></mapper>
        <package name="com.yj.dao" ></package>
            </mappers>
            对这三个标签的解析获取
			*/
				// 如果是package命名
				if ("package".equals(child.getName())) {
					String mapperPackage = child.getStringAttribute("name");
					configuration.addMappers(mapperPackage);
				} else {
					// 一般从这里开始执行
					// 获取resource的属性值  全局配置文件中 mapper标签,属性resource的值
					String resource = child.getStringAttribute("resource");
					// 获取url的属性值
					String url = child.getStringAttribute("url");
					// 获取class的属性值
					String mapperClass = child.getStringAttribute("class");
					// 如果是指定了resource,其它都没有指定,就走这个分支
					if (resource != null && url == null && mapperClass == null) {
						// 将文件路径保存到上下文中,ThreadLocal型
						ErrorContext.instance().resource(resource);
						// 读取文件流
						InputStream inputStream = Resources.getResourceAsStream(resource);
						//
						XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
								configuration.getSqlFragments());
								
					    //解析 *Mapper.xml		
						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.");
					}
				}
			}
		}
	}
5.1mapperParser.parse();

源码

 public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }
configurationElement(parser.evalNode("/mapper"));

源码

private void configurationElement(XNode context) {
    try {
    //获取namespace 判断是否为null 为null抛出异常
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //对非增删改查标签的解析
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      
      //对增删改查标签的解析
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }
buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));

源码

 private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
      buildStatementFromContext(list, null);
  }
  
  //跳转
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  //遍历拿到的所有select|insert|update|delete节点,然后调用statementParser.parseStatementNode()进行解析:
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

statementParser.parseStatementNode();

源码

public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
 
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
 
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
 
    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());
 
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
 
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
 
    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
 
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
 
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");
 
    //这里面就是对整个mapper文件进行了解析操作,包括拿到id;sql语句呀等等,然后将其组装成MappedStatement:
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
builderAssistant.addMappedStatement方法下面部分代码:
id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);
 
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    
   
    MappedStatement statement = statementBuilder.build();
     //将MappedStatement 添加到configuration中 一namespace 为key MappedStatement 为value 
    configuration.addMappedStatement(statement);
    return statement;
bindMapperForNamespace() :

源码

private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        configuration.addMapper(boundType);
      }
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值