Mybaitsソースコード分析(1)コア実行プロセス

この記事では、主にmybaitsのコア実行プロセスのソースコード分析を紹介します。xml構成方法に従ってクエリデモを作成します。テストコードは次のとおりです。

@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();
	}

    上記のコード実行プロセスに従って、実行プロセスを次の部分に分割して分析します: 構成の読み込み、マッパープロキシの作成、SqlSessionの実行

1つは、構成の読み込みです

1.関連クラス
            SqlSessionFactoryBuilder:SqlSessionFactoryオブジェクトを作成し、構成ファイルから解析された構成を渡す役割を果たします。
            XMLConfigBuilder:コア解析クラスとその親クラスBaseBuilderのサブクラスは、さまざまなxml構成をConfigurationオブジェクトに解析する役割を果たします。
            XPathParser:
            XNode:nodeのパッケージを解析するxpath

2.プロセス分析
  1)SqlSessionFactoryBuilderはXMLConfigBuilderを呼び出して、構成への入力ストリームを解析します 

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

 2)parseメソッドは、sqlMapConfig.xmlの分析であり、後でXPathParserとXNodeの使用法によって補完されます。

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)構築プロセスの一部をインターセプトし
      、構成のキーと値のペアを構成にロードします

  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
                }
              }

       インターセプターをロードし、プラグインを解析し、インターセプターのクラス名に従ってインスタンス化し、子要素の構成をロードします

     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);
                  }
               }
          }

2つ目は、マッパープロキシを作成することです。

    1.主な作業クラス:
             MapperRegistry:構成属性(1対1)、作成されたMapperProxyFactoryの管理に使用されます
             MapperProxyFactory:プロキシによって作成されたファクトリクラス、プロキシ実装クラスはMapperProxy、プロキシメソッドの実際の呼び出しクラスはMapperMethodです。
             MapperProxy:InvocationHandlerの拡張機能を実装し、MapperMethodをキャッシュします
             。MapperMethod:元のインターフェースメソッド、構成、セッション、およびその他のパラメーターをラップし、一般的なexcuteメソッドの実行を実現し、それをMapperProxyに使用します。

   2.実行プロセス分析

 1)Mapper、UserMapper mapper = openSession.getMapper(UserMapper.class)を検索します。これは、実際にはMap <Class <?>、MapperProxyFactory <?>> knownMappersで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)newInstanceメソッドはmapperProxyを作成することであり、このmapperProxyは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)上記はプロキシクラスを作成するプロセスです。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)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.要約:
                Mybaits動的プロキシマッパーは、それ自体が設定したマッパーインターフェイスに従って、タイプごとに動的プロキシクラスを作成します。このプロキシクラスによって実際に呼び出されるメソッドは、sqlsessionがインターフェイス+メソッドのIDを呼び出して操作することです。一部のキャッシュ、パラメータ処理、その他の操作。

 3、SqlSession実行プロセス

1.主な職種

            DefaultSqlSession:Wrapping Executor
            Executor:ラッピングトランザクション、コア呼び出しクラス、追加、削除、変更、およびチェック操作はすべて、このクラスによって呼び出されます。
            MappedStatement:パッケージsql、sqlタイプ、戻り値タイプ、パラメーターマッピング(構成ファイルのselectなどのラベルに対応)。
            BoundSql:SQLとパラメーターのマッピングおよびパラメーター値をラップします。(同じタイプのBoundSqlが異なるSqlSourceによって生成されます)。
            StatementHandler:パラメーター処理、SQL実行、戻り値処理を含む、パラメーターの処理と実行。
                ParameterHandler:パラメーター処理
                ResultSetHandler:結果処理
            TypeHandler:型変換

2.実行プロセス

1)selectOneは実際にselectListを呼び出し、パラメーターIDは構成のMappedStatementにあります。

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)キャッシュレイヤーのいくつかの呼び出しは無視されますが、queryFromDatabaseは実際に呼び出されます

			  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)doQueryメソッドは、StatementHandlerを作成し、パラメーターの事前分析を実行し、SQLを実行し、最後に戻り値を処理します。

			 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)パラメーター分析と戻り値処理は、実際にはパラメーター処理を含むStatementHandlerのParameterHandlerとResultSetHandlerの関数であり、TypeHandlerが使用されます。プロセスの省略

インターネット上で盗まれた写真。この写真は、実行プロセスを非常に直感的に示しています。

 

終わり!

 

 

 

 

おすすめ

転載: blog.csdn.net/shuixiou1/article/details/113531162