MyBatis learning notes - source code analysis - analysis process of configuration file

summary

Continued MyBatis learning notes (IV) - source code analysis - analysis process of configuration file (I)  . In the previous article, we introduced the parsing process of properties and settings configuration. Today, we will take a look at the parsing process of other common properties, focusing on the parsing of typeAliases,environments and other configurations.

Parsing process of typeAliases

A simple alias configuration is as follows:

   <typeAliases>
        <typeAlias type="com.jay.chapter2.entity.ClassRoom" alias="ClassRoom"/>
        <typeAlias type="com.jay.chapter2.entity.Student" alias="Student"/>
    </typeAliases>
	
//or    
<typeAliases>
  <package name="domain.blog"/>
</typeAliases> 

As mentioned above, the use of typeAliases configuration is also relatively simple. This configuration is mainly to reduce the redundancy of filling in the fully qualified name in the mapping file. Let's take a look at the parsing process

//* XMLConfigBuilder
  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //If it's package
          String typeAliasPackage = child.getStringAttribute("name");
          //(1) Call typealiasregistry Registeraliases, go to the package to find all classes, and then register the Alias (if there is @ Alias annotation, use it; if not, take the simplename of the class)
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          //If typeAlias
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            //Register the type alias based on the Class name
            //(2) Call typealiasregistry registerAlias
            if (alias == null) {
              //alias can be omitted
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

As above, the execution flow of the entry program method is as follows:

  1. Judge whether it is a package according to the node name. If so, call typealiasregistry Registeraliases, go to the package to find all classes, and then register the Alias (if there is @ Alias annotation, use it; if not, take the simplename of the class)
  2. If not, enter another branch and register the type alias according to the Class name.
    Next, we analyze it according to two branches.

package parse branch

If the package is configured first, then go to find all the package aliases.
So how does it find all the classes under the package? With questions, let's take a look at the source code.

//* TypeAliasRegistry
  public void registerAliases(String packageName){
    registerAliases(packageName, Object.class);
  }
  
    public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    //Scan and register all type aliases inherited from superType under the package
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
	 //
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }
  
  	//Register type alias
  public void registerAlias(Class<?> type) {
    //If there is no type alias, use class Getsimplename to register
    String alias = type.getSimpleName();
	//Or register through Alias annotation (Class.getAnnotation)
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

As mentioned above, the process mainly consists of two parts

  1. Find all classes under the package through the find method of ResolverUtil. The passed in parent class is Object
  2. Register aliases circularly. Only non anonymous classes, non interface and inner classes and non member classes can be registered.
  3. Finally, register alias (alias, type) is called to register alias.
    Then let's take a look at how ResolverUtil finds all classes under the package.
 //The main method is to find all classes that meet the conditions under a package, which are called by typehanderregistry, mapperregistry and typealiasregistry
  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
        //Go deep into the jar package through VFS to find a class
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
		  //Will The class object of class is put into the Set collection for later calls
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }

As mentioned above, the core is to find the sub package under packageName, the class under the package and the class under the sub package through VFS. PS: VFS is a virtual file system used to read the resources in the server. We do not analyze here.

Register and resolve branches according to Class name

	//Register type alias
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    //If the key already exists and the value is inconsistent with the previous one, an error is reported
    //The logic here is a little complicated. I don't think it's necessary. One key is equal to one value. If there is a key, it will directly report an error (the same alias as the built-in type alias in the system will directly report an error)
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }
  
   public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      //Is there a bug in turning lowercase here? See bug 748 (on google code) https://code.google.com/p/mybatis/issues
      //For example, if the local language is Turkish, it is not I when I is capitalized, but another character( İ).  In this way, the machine in Turkey can't use mybatis! This is a big bug, but basically everyone makes it
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      //The principle is very simple. Find the corresponding key value from the HashMap and return the Class corresponding to the type alias
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        //If you can't find it, try to convert String directly to class (no wonder we can also define it directly in the way of java.lang.Integer or int)
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

At this point, the registration and resolution of our type alias are all completed.

Parsing process of environments

As we all know, the environment configuration in MyBatis is mainly used to configure data source information, which is a certain configuration in MyBatis. First, let's take a look at the use of the environments configuration.

    <!-- Set a default connection environment information -->
	<environments default="development">
	 <!--Connect the environment information and take any unique name -->
	  <environment id="development">
	      <!-- mybatis use jdbc Transaction management mode -->
	    <transactionManager type="JDBC">
	      <property name="..." value="..."/>
	    </transactionManager>
		            <!-- mybatis Use connection pooling to get connections -->
	    <dataSource type="POOLED">
		                <!-- Configure the four necessary attributes for interacting with the database -->
	      <property name="driver" value="${driver}"/>
	      <property name="url" value="${url}"/>
	      <property name="username" value="${username}"/>
	      <property name="password" value="${password}"/>
	    </dataSource>
	  </environment>
	</environments>

As mentioned above, after configuring the connection environment information, we must have a question in our mind. How is the parameter ${} resolved? I'll analyze it later.
Let's take a look at the parsing process of this configuration.

. . . . . . . . . . . . . . . . .

For copyright reasons, please refer to the following for the complete article:

MyBatis learning notes (4) -- source code analysis -- analysis process of configuration file (2)

Tags: Java Framework programming language

Posted by mariocesar on Wed, 18 May 2022 01:48:05 +0300