Mybaits source code analysis (1) core execution process

This article mainly introduces the source code analysis of the core execution process of mybaits. We build a query demo according to the xml configuration method. The test code is as follows

@Test
	public void test2() throws Exception {
		InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig2.xml");
		SqlSessionFactory factory2 =  new SqlSessionFactoryBuilder().build(in);
		SqlSession openSession = factory2.openSession();
		UserMapper mapper = openSession.getMapper(UserMapper.class);
		User user = mapper.findUserById(1);
		// User user = openSession.selectOne("com.wj.source_two.demo.mapper.UserMapper.findUserById", 1);
		System.out.println(user);
		openSession.close();
	}

    According to the above code execution process, we divide the execution process into the following parts to analyze:  configuration loading, mapper proxy creation, SqlSession execution

One, configuration loading

1. The related class
            SqlSessionFactoryBuilder: responsible for creating the SqlSessionFactory object and passing in the Configuration parsed from the configuration file.
            XMLConfigBuilder: The core parsing class and the subclass of its parent class BaseBuilder are responsible for parsing various xml configurations to the Configuration object.
            XPathParser: xpath parsing
            the packaging of XNode:node

2. Process analysis
  1) SqlSessionFactoryBuilder calls XMLConfigBuilder to parse the input stream to Configuration 

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
                try {
                  XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
                  return build(parser.parse());

 2) The parse method is the analysis of sqlMapConfig.xml, and the use of XPathParser and XNode is added later.

public Configuration parse() {
                    parseConfiguration(parser.evalNode("/configuration"));
                    return configuration;
              }
              private void parseConfiguration(XNode root) {
                try {
                  propertiesElement(root.evalNode("properties")); //issue #117 read properties first
                  typeAliasesElement(root.evalNode("typeAliases"));
                  pluginElement(root.evalNode("plugins"));
                  objectFactoryElement(root.evalNode("objectFactory"));
                  objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
                  settingsElement(root.evalNode("settings"));
                  environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
                  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);
                }
              }

 3) Intercept part of the construction process and
      load the configuration key-value pair to the configuration

  private void propertiesElement(XNode context) throws Exception {
                if (context != null) {
                  Properties defaults = context.getChildrenAsProperties(); // 得到子节点加载Properties
                  String resource = context.getStringAttribute("resource"); // 得到resource属性
                  String url = context.getStringAttribute("url"); // 得到url属性
                  // ....
                  if (resource != null) { // 从资源文件加载Properties
                    defaults.putAll(Resources.getResourceAsProperties(resource));
                  } else if (url != null) { // 从url属性加载Properties
                    defaults.putAll(Resources.getUrlAsProperties(url));
                  }
                  Properties vars = configuration.getVariables();
                  if (vars != null) {
                    defaults.putAll(vars);
                  }
                  parser.setVariables(defaults);
                  configuration.setVariables(defaults); // 设置到configuration
                }
              }

       Load the interceptor, parse the plugin, and instantiate it according to the class name of the interceptor, and load the configuration of the child element

     private void pluginElement(XNode parent) throws Exception {
         if (parent != null) {
                  for (XNode child : parent.getChildren()) {
                    String interceptor = child.getStringAttribute("interceptor");
                    Properties properties = child.getChildrenAsProperties();
                    Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                    interceptorInstance.setProperties(properties);
                    configuration.addInterceptor(interceptorInstance);
                  }
               }
          }

Two, create a mapper proxy

    1. Main work class:
             MapperRegistry: configuration attribute (1 to 1), used to manage the created MapperProxyFactory
             MapperProxyFactory: factory class created by proxy, proxy implementation class is MapperProxy, proxy method actual call class is MapperMethod.
             MapperProxy: implements the enhancement of InvocationHandler, and caches MapperMethod
             MapperMethod: wraps the original interface method, configuration, session and other parameters, realizes the general excute method execution, and uses it for MapperProxy.

   2. Execution process analysis

 1) Find Mapper, UserMapper mapper = openSession.getMapper(UserMapper.class), it is actually Map<Class<?>, MapperProxyFactory<?>> knownMappers search in MapperRegistry.

      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
                 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
                 // 上面那个方法只是找到已经创建了Mapper接口并缓存到MapperRegistry,没找到报错
                 return mapperProxyFactory.newInstance(sqlSession); // 此方法才是Mapper动态代理的核心
             }

2) The newInstance method is to create a mapperProxy, this mapperProxy implements InvocationHandler

		     public T newInstance(SqlSession sqlSession) {
			    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
			    return newInstance(mapperProxy);
			 }
	    	 protected T newInstance(MapperProxy<T> mapperProxy) {
			    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
			 }	

3) The above is the process of creating a proxy class, let’s focus on the invoke implementation of MapperProxy

			  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			    if (Object.class.equals(method.getDeclaringClass())) { 
			      try { // Object方法直接放行。
			        return method.invoke(this, args);
			      } catch (Throwable t) {
			      }
			    } // 创建MapperMethod并且缓存(缓存到MapperProxy对象中)
			    final MapperMethod mapperMethod = cachedMapperMethod(method);
			    return mapperMethod.execute(sqlSession, args);
			  }

4) Analysis of MapperMethod

 4.1) execute方法一览
			  public Object execute(SqlSession sqlSession, Object[] args) {
			    Object result;
			    if (SqlCommandType.INSERT == command.getType()) {
			      Object param = method.convertArgsToSqlCommandParam(args);
			      result = rowCountResult(sqlSession.insert(command.getName(), param));
			    } else if (SqlCommandType.UPDATE == command.getType()) {
			      Object param = method.convertArgsToSqlCommandParam(args);
			      result = rowCountResult(sqlSession.update(command.getName(), param));
			    } else if (SqlCommandType.DELETE == command.getType()) {
			      Object param = method.convertArgsToSqlCommandParam(args);
			      result = rowCountResult(sqlSession.delete(command.getName(), param));
			    } else if (SqlCommandType.SELECT == command.getType()) {
			      if (method.returnsVoid() && method.hasResultHandler()) {
			      } else if (method.returnsMany()) {
			        result = executeForMany(sqlSession, args); // 查询方法
			      } else if (method.returnsMap()) {
			      } else {
			      }
			    } else {
			      throw new BindingException("Unknown execution method for: " + command.getName());
			    }
			    return result;
			  }		
			 4.2) 我们从 execute方法看到	insert方法是调用session.insert(command.getName,param),这个name是什么?
			  public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
			 	 String statementName = mapperInterface.getName() + "." + method.getName(); //就是接口名+方法名
			 4.3)  在看看查询的方法 result = executeForMany(sqlSession, args); 
			   private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
			    List<E> result;
			    // 转换参数
			    Object param = method.convertArgsToSqlCommandParam(args);
			    // 执行查询
			    result = sqlSession.<E>selectList(command.getName(), param);
			    // .....
			    return result;
			  }

5. Summary:
                Mybaits dynamic proxy mapper is to create a dynamic proxy class for each type according to the mapper interface set by itself. The method actually called by this proxy class is that sqlsession calls the id of interface+method to operate, which combines some Cache, parameter processing and other operations.

 Three, SqlSession execution process

1. Main job categories

            DefaultSqlSession: Wrapping Executor
            Executor: Wrapping Transaction, core calling classes, adding, deleting, modifying, and checking operations are all called by this class.
            MappedStatement : package sql, sql type, return value type, parameter mapping (corresponding to a label such as a select in the configuration file).
            BoundSql: wrap sql and parameter mapping and parameter values. (The same type of BoundSql is generated by different SqlSource).
            StatementHandler: Parameter processing and execution, including parameter processing, sql execution, and return value processing.
                ParameterHandler: parameter processing
                ResultSetHandler: result processing
            TypeHandler: type conversion

2. The execution process

1) SelectOne actually calls selectList, and the parameter id can be found in the MappedStatement from the Configuration.

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
			  // 找mapperStatement
		      MappedStatement ms = configuration.getMappedStatement(statement); 
		      // 调用executor执行查询方法query。
		      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
		      return result;
			}

2) Several calls of the cache layer are ignored, but queryFromDatabase is actually called

			  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
			    List<E> list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
			    return list;
			  }

3) The doQuery method is to create StatementHandler, then execute parameter pre-analysis, execute sql, and finally handle the return value

			 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
			    Statement stmt = null;
			    try {
			      Configuration configuration = ms.getConfiguration();
			      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
			      stmt = prepareStatement(handler, ms.getStatementLog());
			      return handler.<E>query(stmt, resultHandler);
			    } finally {
			      closeStatement(stmt);
			    }
			  }

4) Parameter analysis and return value processing are actually the functions of StatementHandler's ParameterHandler and ResultSetHandler, which involve parameter processing, and TypeHandler is used. Process omission

A stolen picture on the Internet, this picture is very intuitive to show the execution process.

 

end!

 

 

 

 

Guess you like

Origin blog.csdn.net/shuixiou1/article/details/113531162