Análisis del código fuente de mybaits (1) proceso de ejecución del núcleo

Este artículo presenta principalmente el análisis del código fuente del proceso de ejecución central de mybaits. Creamos una demostración de consulta de acuerdo con el método de configuración xml. El código de prueba es el siguiente

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

    De acuerdo con el proceso de ejecución de código anterior, dividimos el proceso de ejecución en las siguientes partes para analizar:  carga de configuración, creación de proxy de mapeador, ejecución de SqlSession

Uno, carga de configuración

1. La clase relacionada
            SqlSessionFactoryBuilder: responsable de crear el objeto SqlSessionFactory y pasar la Configuración analizada desde el archivo de configuración.
            XMLConfigBuilder: la clase de análisis principal y la subclase de su clase principal, BaseBuilder, son responsables de analizar varias configuraciones xml para el objeto de configuración.
            XPathParser: xpath analizando
            el paquete de XNode: node

2. Análisis de proceso
  1) SqlSessionFactoryBuilder llama a XMLConfigBuilder para analizar el flujo de entrada a Configuración 

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

 2) El método de análisis es el análisis de sqlMapConfig.xml, complementado por el uso de XPathParser y XNode más adelante.

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) Interceptar parte del proceso de construcción y
      cargar el par clave-valor de configuración en la configuración

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

       Cargue el interceptor, analice el complemento y cree una instancia de acuerdo con el nombre de clase del interceptor y cargue la configuración del elemento secundario

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

Dos, crea un proxy de mapeador

    1. Clase de trabajo principal:
             MapperRegistry: atributo de configuración (1 a 1), utilizado para administrar el MapperProxyFactory creado
             MapperProxyFactory: clase de fábrica creada por proxy, la clase de implementación del proxy es MapperProxy, la clase de llamada real del método proxy es MapperMethod.
             MapperProxy: implementa la mejora de InvocationHandler y almacena en caché MapperMethod
             MapperMethod: envuelve el método de interfaz original, la configuración, la sesión y otros parámetros, realiza la ejecución del método general excute y lo usa para MapperProxy.

   2. Análisis del proceso de ejecución

 1) Find Mapper, UserMapper mapper = openSession.getMapper (UserMapper.class), que en realidad es Map <Class <?>, MapperProxyFactory <? >> búsqueda de knownMappers en 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) El método newInstance es crear un mapperProxy, este mapperProxy implementa 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) Lo anterior es el proceso de creación de una clase de proxy, centrémonos en la implementación de invocación de 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) Análisis de 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. Resumen:
                El mapeador de proxy dinámico de Mybaits es crear una clase de proxy dinámico para cada tipo de acuerdo con la interfaz del mapeador establecida por sí mismo. El método realmente llamado por esta clase de proxy es que sqlsession llama a la identificación de la interfaz + método para operar, que combina algunas operaciones de caché, procesamiento de parámetros y otras.

 Tres, proceso de ejecución de SqlSession

1. Principales categorías de puestos

            DefaultSqlSession: Wrapping Executor
            Executor: Wrapping Transaction, clases de llamadas centrales, operaciones de adición, eliminación, modificación y verificación son todas llamadas por esta clase.
            MappedStatement : paquete sql, tipo sql, tipo de valor de retorno, mapeo de parámetros (correspondiente a una etiqueta como una selección en el archivo de configuración).
            BoundSql: envuelve sql y mapeo de parámetros y valores de parámetros. (El mismo tipo de BoundSql es generado por diferentes SqlSource).
            StatementHandler: procesamiento y ejecución de parámetros, incluido el procesamiento de parámetros, la ejecución de sql y el procesamiento del valor de retorno.
                ParameterHandler: procesamiento de parámetros
                ResultSetHandler: procesamiento de resultados
            TypeHandler: conversión de tipos

2. El proceso de ejecución

1) selectOne en realidad llama a selectList, y la identificación del parámetro se puede encontrar en MappedStatement desde la Configuración.

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) Se ignoran varias llamadas de la capa de caché, pero en realidad se llama a 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) El método doQuery es crear StatementHandler, luego ejecutar el análisis previo de parámetros, ejecutar sql y finalmente manejar el valor de retorno

			 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) El análisis de parámetros y el procesamiento del valor de retorno son en realidad las funciones de ParameterHandler y ResultSetHandler de StatementHandler, que implican el procesamiento de parámetros, y se utiliza TypeHandler. Omisión de proceso

Una imagen robada en Internet, esta imagen es muy intuitiva para mostrar el proceso de ejecución.

 

¡fin!

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/113531162
Recomendado
Clasificación