`

Mybatis源码分析别名实现机制。

阅读更多
在Mybatis中我们经常会用到将某个bean作为参数类型parameterType或者结果返回值类型ResultType,所以很多时候我们需要把完成的Bean的包名在mapper文件中

<select id="selectUser" parameterType="com.test.entity.User" resultType="com.test.entity.User">  
    select * from user where c_id=#{id}  
</select>  


Mybatis给我们提供了一种叫别名的机制,意思就是对某个具体的类设置别名,在mybatis的配置文件中配置如下:
<configuration>
    <typeAliases>
      <!--
      通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的javabean,
      并且默认设置一个别名,默认的名字为: javabean 的首字母小写的非限定类名来作为它的别名。
      也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user) 
      <package name="com.test.entity"/>
       -->
      <typeAlias alias="user" type="com.test.entity.User"/>
  </typeAliases>
  
  ......
  
</configuration>


这样之后mapper文件中的select可以写成如下格式:

<select id="selectUser" parameterType="user" resultType="user">  
    select * from user where c_id=#{id}  
</select>  



Mybatis相关的解析源码:

//类别名解析
 
private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
		//如果子节点是package,那么就获取package节点的name属性
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
		//如果子节点是typeAlias节点,那么就获取alias属性和type的属性
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
			//通过type的值来加载获得类
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
			//typeAliasRegistry会进行别名注册
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }


解析alias来获得别名,解析type元素来获得类名,通过Resources.classForName(type)获得类信息,然后通过typeAliasRegistry.registerAlias(alias, clazz)将类别名注册到typeAliasRegistry中,这样就完成了mybatis中配置文件的解析。


TypeAliasRegistry类源码分析
TypeAliasRegistry:是用来记录别名alias和类clazz之间的对应关系的,它可以看做是一个Map,
alias作为key,类名作为value,详看源码如下:

public class TypeAliasRegistry {

  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

  @SuppressWarnings("unchecked")
  // throws class cast exception as well if types cannot be assigned
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }


  
  public void registerAliases(String packageName){
    registerAliases(packageName, Object.class);
  }



  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }

  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

  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 (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 void registerAlias(String alias, String value) {
    try {
      registerAlias(alias, Resources.classForName(value));
    } catch (ClassNotFoundException e) {
      throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
    }
  }
  
  /**
   * @since 3.2.2
   */
  public Map<String, Class<?>> getTypeAliases() {
    return Collections.unmodifiableMap(TYPE_ALIASES);
  }

}




解析mybatis的配置文件,将alias元素的值作为Map的key,通过反射机制获得的type元素对应的类名的类作为Map的value值,在真正使用时通过alias别名来获取真正的类。

但是如果配置package情况要复杂的多,下面看下配置package的处理过程。

 public void registerAliases(String packageName){
    registerAliases(packageName, Object.class);
  }

  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();


    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }


我们继续分析ResolverUtil.java的方法,这里IsA是个闭包,里面主要实现一个类视为为parent的子类。 find函数会遍历这个package下面所有的class,如果是Object的子类并切能被classloader加载
  缓存起来, resolverUtil.getClasses()就是返回我们缓存的class。

public static class IsA implements Test {
    private Class<?> parent;

    /** Constructs an IsA test using the supplied Class as the parent class/interface. */
    public IsA(Class<?> parentType) {
      this.parent = parentType;
    }

    @Override
    public boolean matches(Class<?> type) {
      return type != null && parent.isAssignableFrom(type);
    }

    @Override
    public String toString() {
      return "is assignable to " + parent.getSimpleName();
    }
  }


   public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }

  protected String getPackagePath(String packageName) {
    return packageName == null ? null : packageName.replace('.', '/');
  }

   public List<String> list(String path) throws IOException {
    List<String> names = new ArrayList<String>();
    for (URL url : getResources(path)) {
      names.addAll(list(url, path));
    }
    return names;
  }

       protected static List<URL> getResources(String path) throws IOException {
    		return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
 	 }


   
protected void addIfMatching(Test test, String fqn) {
    try {
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }

      Class<?> type = loader.loadClass(externalName);
      if (test.matches(type)) {
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
      log.warn("Could not examine class '" + fqn + "'" + " due to a " +
          t.getClass().getName() + " with message: " + t.getMessage());
    }
  }

分享到:
评论

相关推荐

    mybatis3.x源码深度解析与最佳实践.pdf

    本系列源码解析的方式将从 MyBatis 的源码入手,深入分析 MyBatis 的设计思路和实现机制。 2. 容器的加载与初始化 2.1 config 文件解析 XMLConfigBuilder.parseConfiguration 方法是 MyBatis 中的核心方法之一,...

    Mybatis源码分析.md

    ### Mybatis源码分析 #### 1. 解析配置文件,创建SQLSessionFactory 在MyBatis框架中,创建`SQLSessionFactory`是初始化整个框架的重要步骤之一。这一过程涉及到了配置文件的读取与解析,以及如何构建出可以用于...

    Mybatis源码讲解,面向秋招

    Mybatis 是一个流行的 Java 持久层框架,它简化了数据库操作,通过 XML 或注解方式将 SQL 语句与 Java 代码分离。在面试中,了解 Mybatis ...在实际开发中,掌握 Mybatis 的源码分析和优化技巧也是提升项目性能的关键。

    尚硅谷 4月份MyBatis视频教程(国内首套:源码级讲授的MyBatis视频)最新更新

    尚硅谷发布的这套MyBatis视频教程,是国内首套源码级讲授的MyBatis教程,非常适合希望深入了解MyBatis内部原理及实现机制的学习者。通过本篇总结,我们不仅了解了MyBatis的基本概念、核心功能,还探讨了一些实战中的...

    MyBatis 中文帮助文档以及MyBatis包

    11. **插件扩展**:MyBatis提供插件机制,可以通过拦截器实现对SqlSession或Executor等对象的方法拦截,用于日志记录、性能分析等。 12. **最佳实践**:在实际使用中,应遵循最佳实践,如避免全表扫描、合理设计SQL...

    mybatis-3.2.0说明文档

    MyBatis的日志记录机制允许开发者跟踪SQL语句的执行情况,这对于调试和性能分析非常重要。MyBatis支持多种日志框架,如Log4j、SLF4J等,可以根据项目需求选择合适的日志框架进行集成。 以上是mybatis-3.2.0说明文档...

    mybatis-3.4.1(jar所有包下载)

    在分析MyBatis源码时,可以了解其实现原理,加深对ORM框架的理解,有助于优化数据库操作和提高开发效率。通过下载的mybatis-3.4.1.zip和mybatis-3-mybatis-3.4.1.zip文件,开发者可以直接查看源代码,学习如何集成到...

    mybatis 基于配置2

    MyBatis的源码分析可以帮助开发者理解其工作原理,提高问题排查能力。同时,它也是一款强大的工具,能有效提升开发效率。 在"mybatis-02"这个压缩包文件名中,我们可以猜测这是系列教程或示例的一部分,可能包含了...

    mybatis02.zip

    9. **插件扩展**:通过拦截器插件机制,用户可以自定义插件,实现日志、性能分析等功能。 10. **事务管理**:MyBatis允许用户自定义事务管理器,可以配合Spring等框架实现声明式事务管理。 如果压缩包中包含示例...

    mybatistest.zip

    在本实例中,“mybatistest.zip”包含了基于MyBatis官网教程创建的一个小型项目,目的是为了帮助开发者更好地理解和使用MyBatis源码。下面我们将深入探讨这个实例中的关键知识点。 1. **MyBatis简介** MyBatis消除...

    proxool源码

    **Proxool源码分析** Proxool是一个开源的、基于Java的数据库连接池,它在2003年由iBatis(现在的MyBatis)团队开发并维护,旨在提供一个轻量级、高效的数据库连接管理解决方案。在深入研究Proxool源码之前,我们先...

Global site tag (gtag.js) - Google Analytics