MyBatis的TypeAlias和TypeHandler实现原理概述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lichunericli/article/details/82317638

MyBatis中的别名是用来简写输入输出类型的全路径的定义,存在两种方式来注册别名:
1)XML方式:<typeAlias alias="User" type="XXX.User"/>                                                                                                  2)Annotation方式:@Alias("user")public class User{ }

1. TypeAlias的解析注册
别名的处理是通过XMLConfigBuilder中的typeAliasesElement(root.evalNode("typeAliases"))来实现的,其实现源码如下所示:
private void typeAliasesElement(XNode parent) {
  for (XNode child : parent.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeAliasPackage = child.getStringAttribute("name");
      configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
    } else {
      if (alias == null) {
         typeAliasRegistry.registerAlias(clazz);
      } else {
         typeAliasRegistry.registerAlias(alias, clazz);
      }
    }
  }
}

别名的解析最终都是注册到TypeAliasRegistry的Map<String, Class<?>> TYPE_ALIASES = new HashMap<>():
public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName();
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  } 
  registerAlias(alias, type); // TYPE_ALIASES.put(alias.toLowerCase(Locale.ENGLISH), value);
}
由源码可知如果不存在别名则取类名为别名,如果存在Alias注解则取Alias注解的Value值,即上文的user值。在Configuration和
TypeAliasRegistry中都有默认的别名注册实现,可直接在项目中使用:
public TypeAliasRegistry() {
  registerAlias("string", String.class);
  // ......
}

2. TypeHandler的解析注册
类型解析器是通过XMLConfigBuilder中typeHandlerElement(root.evalNode("typeHandlers"))实现的,其实现源码如下所示:  
private void typeHandlerElement(XNode parent) throws Exception {
  for (XNode child : parent.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeHandlerPackage = child.getStringAttribute("name");
      typeHandlerRegistry.register(typeHandlerPackage); // 忽略内部类,接口和抽象类以及package-info.java类
      // Java类的注解:typeHandlerClass.getAnnotation(MappedTypes.class); 
    } else {
      if (javaTypeClass != null) {
        if (jdbcType == null) {
          typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
      // JDBC类的注解:typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
        } else {
          typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
        }
      } else {
       typeHandlerRegistry.register(typeHandlerClass);
      }
    }
  }
}

类型解析器的解析方式多种,最终都是注册到TypeHandlerRegistry的对应的Map中:
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
在TypeHandlerRegistry中有默认的类型解析器注册实现,可直接在项目中使用:
public TypeHandlerRegistry() {
  register(Boolean.class, new BooleanTypeHandler());
  // ......
}

3. 总结
MyBatis提供的TypeHandler<T>接口存在4个方法:参数的设置,从存储过程中取值,根据索引号或名称从ResultSet结果集中取值。可通过Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values())来确保TypeHandler的获取是线程安全的。在实际运用中可以使用自定义TypeHandler来处理枚举值的转换或者状态值的转换,其实现只需要继承BaseTypeHandler类,实现其对应方法即可。MyBatis对于SqlRunner的执行是通过JDK底层的JDBC来实现的,但是Parameters参数的设置或者结果集ResultSet的获取都是通过TypeHandler来实现的。

所有的TypeAlias都需要注册到注册机TYPE_ALIASES中,对于TypeAlias的解析首先将key转成小写,然后从TYPE_ALIASES中取得对应的Class,如果获取不到则将key直接转成Class,这也是为什么使用java.lang.Integer或int都可以的逻辑。实际运用中如果需要处理频繁获取枚举值的操作,可以在类初始化时将对应的枚举值key-value存进静态私有变量HashMap中,从内存HashMap获取值可以提高性能。

猜你喜欢

转载自blog.csdn.net/lichunericli/article/details/82317638
今日推荐