MyBatis源碼解析 - 類型轉(zhuǎn)換模塊

MyBatis源碼解析 - 類型轉(zhuǎn)換模塊

前言

JDBC數(shù)據(jù)類型與Java語言中的數(shù)據(jù)類型并不是完全對(duì)應(yīng)的斟叼,所以在PreparedStatement為SQL語句綁定參數(shù)時(shí)萧落,需要從Java類型轉(zhuǎn)換成JDBC類型怎虫,而從結(jié)果集中獲取數(shù)據(jù)時(shí),則需要從JDBC類型轉(zhuǎn)換成Java類型柏肪。MyBatis 使用類型處理器完成上述兩種轉(zhuǎn)換,如圖所示芥牌。

在MyBatis中使用JdbeType這個(gè)枚舉類型代表JIDBC中的數(shù)據(jù)類型烦味,該枚舉類型中定義了TYPECODE字段,記錄了JDBC類型在javasql.Types中相應(yīng)的常量編碼壁拉,并通過一個(gè)靜態(tài)集合codelookup(HashMap<nteger,JdbeTypec>類型)維護(hù)了常量編碼與JdbcType之間的對(duì)應(yīng)關(guān)系谬俄。

TypeHandler

TypeHandler是類型處理器接口,MyBatis中所有的類型轉(zhuǎn)換器都繼承了TypeHandler接口弃理,在TypeHandler接口中定義了如下四個(gè)方法溃论,這四個(gè)方法分為兩類:setParameter()方法負(fù)責(zé)將數(shù)據(jù)由JdbeType類型轉(zhuǎn)換成Java類型;getResult()方法及其重載負(fù)責(zé)將數(shù)據(jù)由Java類型轉(zhuǎn)換成JdbcType類型。

public interface TypeHandler<T> {

  //在通過PreparedStatement為SQL語句綁定參數(shù)時(shí)痘昌,會(huì)將數(shù)據(jù)由JdbcType類型轉(zhuǎn)換成Java類型
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  //從 ResultSet 中獲取數(shù)據(jù)時(shí)會(huì)調(diào)用此方法蔬芥,會(huì)將數(shù)據(jù)由Java類型轉(zhuǎn)換成JdbcType類型
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

為方便用戶自定義TypeHandler實(shí)現(xiàn)梆靖,MyBatis 提供了BaseTypeHandler 這個(gè)抽象類,它實(shí)現(xiàn)了TypeHandler接口笔诵,并繼承了TypeReference抽象類返吻,其繼承結(jié)構(gòu)如圖所示。

<img src="http://qiniu-cdn.janker.top/oneblog/20200117165004531.jpg" style="zoom:33%;" />

BaseTypeHandler中實(shí)現(xiàn)TypeHandler.setParameter()方法和TypeHandler.getResult()方法, 具體實(shí)現(xiàn)如下所示乎婿。需要注意的是测僵,這兩個(gè)方法對(duì)于非空數(shù)據(jù)的處理都交給了子類實(shí)現(xiàn)。

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
  if (parameter == null) {
    if (jdbcType == null) {
      //參數(shù) 類型 異常
      throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
    }
    try {
      //綁定參數(shù)為null的處理
      ps.setNull(i, jdbcType.TYPE_CODE);
    } catch (SQLException e) {
      throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
            + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
            + "Cause: " + e, e);
    }
  } else {
    try {
      //綁定非空參數(shù) 該方法為抽象方法 由子類實(shí)現(xiàn)
      setNonNullParameter(ps, i, parameter, jdbcType);
    } catch (Exception e) {
      throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
            + "Try setting a different JdbcType for this parameter or a different configuration property. "
            + "Cause: " + e, e);
    }
  }
}

public T getResult(ResultSet rs, String columnName) throws SQLException {
  try {
    //抽象方法 由多個(gè)重載 由子類實(shí)現(xiàn)
    return getNullableResult(rs, columnName);
  } catch (Exception e) {
    throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
  }
}

BaseTypeHandler的實(shí)現(xiàn)類比較多谢翎,如圖所示捍靠,但大多是直接調(diào)用PreparedStatementResultSetCallableStatement的對(duì)應(yīng)方法,實(shí)現(xiàn)比較簡單森逮。

<img src="/Users/janker/Library/Application Support/typora-user-images/image-20200118165213276.png" style="zoom:50%;" />

這里以IntegerTypeHandler為例簡單介紹榨婆,其他的實(shí)現(xiàn)類請(qǐng)讀者參考相關(guān)源碼:

public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    //調(diào)用PreparedStatement.setInt()實(shí)現(xiàn)參數(shù)綁定
    ps.setInt(i, parameter);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    //調(diào)用PreparedStatement.getInt()獲取指定列值
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }

  @Override
  public Integer getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    //調(diào)用ResultSet.getInt()獲取指定列值
    int result = rs.getInt(columnIndex);
    return result == 0 && rs.wasNull() ? null : result;
  }

  @Override
  public Integer getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    //調(diào)用CallableStatement.getInt()獲取指定列值
    int result = cs.getInt(columnIndex);
    return result == 0 && cs.wasNull() ? null : result;
  }
}

一般情況下, TypeHandler用于完成單個(gè)參數(shù)以及單個(gè)列值的類型轉(zhuǎn)換褒侧,如果存在多列值轉(zhuǎn)換成一個(gè)Java對(duì)象的需求良风,應(yīng)該優(yōu)先考慮使用在映射文件中定義合適的映射規(guī)則(<resultMap>節(jié)點(diǎn))完成映射。

TypeHandlerRegistry

介紹完TypeHandler接口及其功能后闷供,我們來看一下MyBatis如何管理眾多的TypeHandler接口實(shí)現(xiàn)烟央,如何知道何時(shí)使用哪個(gè)TypeHandler接口完成轉(zhuǎn)換。這些都是由TypehandlerRegistry完成的歪脏,在MyBatis初始化過程中疑俭,會(huì)為所有已知的TypeHandler創(chuàng)建對(duì)象,并實(shí)現(xiàn)注冊(cè)到TypeHandlerRegistry中婿失,由TypeHandlerRegistry負(fù)責(zé)掛你這些TypeHandler對(duì)象钞艇。

TypeHandlerRegistry的核心字段的含義如下:

//記錄JdbcType與TypeHandler之間的對(duì)應(yīng)關(guān)系,其中JdbcType是一個(gè)枚舉類型豪硅,它定義對(duì)應(yīng)的JDBC類型
//該集合主要用于從結(jié)果集讀取數(shù)據(jù)時(shí)香璃,將數(shù)據(jù)從Jdbc類型轉(zhuǎn)換成Java類型
private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
//記錄了Java類型向指定JdbcType轉(zhuǎn)換時(shí),需要使用的TypeHandler對(duì)象。例如: Java 類型中的String可能
//轉(zhuǎn)換成數(shù)據(jù)庫的char舟误、varchar等多種類型葡秒,所以存在一對(duì)多關(guān)系
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final TypeHandler<Object> unknownTypeHandler;
//記錄了全部TypeHandler類型以及該類型相應(yīng)的TypeHandler對(duì)象
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

//空TypeHandler集合的標(biāo)識(shí)
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

1. 注冊(cè)TypeHandler對(duì)象

TypeHandlerRegistry.register()方法實(shí)現(xiàn)了注冊(cè)TypeHandler對(duì)象的功能,該注冊(cè)過程會(huì)向上述四個(gè)集合中添加TypeHandler對(duì)象嵌溢。register()方法有多個(gè)重載眯牧,這些重載之間的調(diào)用關(guān)系如圖所示。

<img src="http://qiniu-cdn.janker.top/oneblog/20200119114322840.jpg" style="zoom:33%;" />

從圖中可以看出赖草,多數(shù)register()方法最終會(huì)調(diào)用重載④完成注冊(cè)功能学少,我們先來介紹該方法的實(shí)現(xiàn),其三個(gè)參數(shù)分別指定了TypeHandler 能夠處理的Java 類型秧骑、Jdbc 類型以及TypeHandler對(duì)象版确。

//register()方法的重載④的實(shí)現(xiàn)如下:
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) { //檢驗(yàn)是否明確指定了TypeHandler能夠處理的Java類型
    //獲取指定Java類型在TYPE_ HANDLER_ MAP集合中對(duì)應(yīng)的TypeHandler集合
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      //創(chuàng)建新的TypeHandler集合扣囊,并添加到TYPE HANDLER MAP中
      map = new HashMap<>();
      typeHandlerMap.put(javaType, map);
    }
    //將TypeHandler對(duì)象注冊(cè)到typeHandlerMap集合中
    map.put(jdbcType, handler);
  }
  //向AllTypeHandler集合注冊(cè)TypeHandler類型和對(duì)應(yīng)的TypeHandler對(duì)象【以Class為key handler對(duì)象為value】
  allTypeHandlersMap.put(handler.getClass(), handler);
}

在①~③這個(gè)三個(gè)registr()方法重載中會(huì)嘗試讀取TypeHandler類中定義的@MappedTypes注解和@MappedJdbcTypes注解,@MappedTypes 注解用于指明該TypeHandler實(shí)現(xiàn)類能夠處理的Java類型的集合绒疗,@MappedJdbcTypes注解用于指明該TypeHandler實(shí)現(xiàn)類能夠處理的JDBC類型集合侵歇。register()方法的重載①~③的具體實(shí)現(xiàn)如下:

//register()方法的重載①的實(shí)現(xiàn)如下:
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
//獲取 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
  for (Class<?> javaTypeClass : mappedTypes.value()) {
    //經(jīng)過強(qiáng)制類型轉(zhuǎn)換 以及反射創(chuàng)建TypeHandler對(duì)象之后,交由重載③繼續(xù)處理
    register(javaTypeClass, typeHandlerClass);
    mappedTypeFound = true;
  }
}
if (!mappedTypeFound) {
  //未@MappedTypes 注解吓蘑,調(diào)用重載方法 ② 處理
  register(getInstance(null, typeHandlerClass));
}
}
//register()方法的重載②的實(shí)現(xiàn)如下:
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
//獲取@MappedTypes注解 并根據(jù)MappedTypes注解指定的java類型進(jìn)行注冊(cè)惕虑,邏輯與重載①類似
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
  for (Class<?> handledType : mappedTypes.value()) {
    //交由重載③進(jìn)行處理
    register(handledType, typeHandler);
    mappedTypeFound = true;
  }
}
//從3.1.0版本開始,可以根據(jù)TypeHandler類型自動(dòng)查找對(duì)應(yīng)的Java類型磨镶,這需要
//我們的TypeHandler實(shí)現(xiàn)類同時(shí)繼承TypeReference這個(gè)抽象類
// @since 3.1.0 - try to auto-discover the mapped type
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
  try {
    TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
    //交由重載③進(jìn)行處理
    register(typeReference.getRawType(), typeHandler);
    mappedTypeFound = true;
  } catch (Throwable t) {
    // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
  }
}
if (!mappedTypeFound) {
  //類型轉(zhuǎn)換后溃蔫,交由重載③繼續(xù)處理
  register((Class<T>) null, typeHandler);
}
}
//register()方法的重載③的實(shí)現(xiàn)如下:
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//獲取MappedJdbcTypes注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
  //根據(jù)指定類型進(jìn)行注冊(cè) 讀取@MappedJdbcTypes value值 獲取handlerJdbcType
  for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
    register(javaType, handledJdbcType, typeHandler);
  }
  if (mappedJdbcTypes.includeNullJdbcType()) {
    register(javaType, null, typeHandler);
  }
} else {
  //未指定MappedJdbcTypes注解 交由重載④繼續(xù)處理
  register(javaType, null, typeHandler);
}
}

上述全部的register()方法重 載都是在向TYPE_ HANDLER_ _MAP集合和ALL_TYPE_HANDLERS MAP集合注冊(cè)TypeHandler 對(duì)象,而重載⑤是向JDBC_ TYPE_ HANDLER_ MAP集合注冊(cè)TypeHandler對(duì)象琳猫,其具體實(shí)現(xiàn)如下:

//register()方法重載⑤的實(shí)現(xiàn)如下:
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
// 注冊(cè)JdbcType類型對(duì)一個(gè)的TypeHandler
jdbcTypeHandlerMap.put(jdbcType, handler);
}

TypeHandlerRegistry除了提供注冊(cè)單個(gè)TypeHandlerregister()重載,還可以掃描整個(gè)包下的TypeHandler接口實(shí)現(xiàn)類伟叛,并將完成這些TypeHandler實(shí)現(xiàn)類的注冊(cè)。下面來看重載⑥的具體實(shí)現(xiàn):

//register()方法重載⑥的實(shí)現(xiàn)脐嫂,主要用來自動(dòng)掃描指定包下的TypeHandler實(shí)現(xiàn)并完成注冊(cè)
public void register(String packageName) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  //查找指定包下的TypeHandler接口實(shí)現(xiàn)類
  resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  for (Class<?> type : handlerSet) {
    //Ignore inner classes and interfaces (including package-info.java) and abstract classes
    //過濾調(diào)內(nèi)部類统刮、接口以及抽象類
    if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
      //交由重載①繼續(xù)后面注冊(cè)操作
      register(type);
    }
  }
}

最后來看TypeHandlerRegistry構(gòu)造方法,會(huì)通過上述register()方法為很多基礎(chǔ)類型注冊(cè)對(duì)應(yīng)的TypeHandler對(duì)象雹锣,簡略代碼如下:

public TypeHandlerRegistry(Configuration configuration) {
    //...這里重點(diǎn)看下String相關(guān)的TypeHandler對(duì)象的注冊(cè) 其他類型的TypeHandler的注冊(cè)類型(略)
    // stringTypeHandler 能夠?qū)?shù)據(jù)從String 類型轉(zhuǎn)換成null ( JdbcType )网沾,所以向
    TYPE HANDLER MAP
    //集合注冊(cè)該對(duì)象癞蚕,并向ALL_ TYPE_ HANDLERS_ MAP集合注冊(cè)StringTypeHandler
    register(String.class, new StringTypeHandler());
    
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    //向JDBC_TYPE_HANDLER_MAP集合注冊(cè)NVARCHAR對(duì)應(yīng)的NStringTypeHandler
  register(JdbcType.NVARCHAR, new NStringTypeHandler());
  // ...注冊(cè)其他JdbcType類型對(duì)應(yīng)的TypeHandler的過程類似(略)
}

2. 查找TypeHandler對(duì)象

介紹完注冊(cè)TypeHandler對(duì)象的功能之后蕊爵,再來介紹TypeHandlerRegistry提供的查找TypeHandler對(duì)象的功能。TypeHandlerRegistry.getTypeHandler()方法實(shí)現(xiàn)了從上述四個(gè)集合中獲取對(duì)應(yīng)TypeHandler對(duì)象的功能桦山。TypeHandlerRegistry.getTypeHandler()方法有多個(gè)重載,這些重載之間的關(guān)系如圖所示攒射。

<img src="http://qiniu-cdn.janker.top/oneblog/20200119134802598.png" style="zoom:40%;" />

經(jīng)過一系列類型轉(zhuǎn) 換之后,TypeHandlerRegistry.getTypeHandler()方法的多個(gè)重載都會(huì)調(diào)用TypeHandlerRegistry.getTypeHandle(Type,JdbcType)這個(gè)重載方法恒水,它會(huì)根據(jù)指定的Java類型和JdbcType類型查找相應(yīng)的TypeHandler對(duì)象会放,具體實(shí)現(xiàn)如下:

private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  if (ParamMap.class.equals(type)) {
    return null;
  }
  //查找或初始化Java類型對(duì)一個(gè)的TypeHandler對(duì)象
  Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
  TypeHandler<?> handler = null;
  if (jdbcHandlerMap != null) {
    //根據(jù)JdbcType查找TypeHandler
    handler = jdbcHandlerMap.get(jdbcType);
    if (handler == null) {
      handler = jdbcHandlerMap.get(null);
    }
    if (handler == null) {
      // #591
      //如果JdbcType只注冊(cè)了一個(gè)TypeHandler,使用此TypeHandler對(duì)象
      handler = pickSoleHandler(jdbcHandlerMap);
    }
  }
  // type drives generics here
  return (TypeHandler<T>) handler;
}

TypeHandlerRegitry.getJdbcHandlerMap()方法中钉凌,會(huì)檢測TYPE_HANDLER_MAP集合中指定Java類型對(duì)應(yīng)的TypeHandler集合是否已經(jīng)初始化咧最。如果未初始化,則嘗試以該Java類型的御雕、已初始化的父類對(duì)應(yīng)的TypeHandler集合為初始集合;如不存在己初始化的父類矢沿,則將其對(duì)應(yīng)的TypeHandler集合初始化為NULL_TYPE_HANDLER_MAP標(biāo)識(shí)。getJdbcHandlerMap()方法具體實(shí)現(xiàn)如下:

private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
    //查找指定Java類型對(duì)應(yīng)的TypeHandler集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
    if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) { //檢測是否為空集合標(biāo)識(shí)
      return null;
    }
    //初始化指定的Java類型的TypeHandler集合
    if (jdbcHandlerMap == null && type instanceof Class) {
      Class<?> clazz = (Class<?>) type;
      if (Enum.class.isAssignableFrom(clazz)) {
        Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;
        jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);
        if (jdbcHandlerMap == null) {
          //枚舉類型的處理
          register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));
          return typeHandlerMap.get(enumClass);
        }
      } else {
        //查找父類對(duì)應(yīng)的TypeHandler集合酸纲,并作為初始化集合
        jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
      }
    }
    // 以NULL_TYPE_HANDLER_MAP作為TypeHandler集合
    typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
    return jdbcHandlerMap;
  }
  
  // getJdbcHandlerMapForSuperclass()實(shí)現(xiàn)
  private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
    Class<?> superclass =  clazz.getSuperclass();
    if (superclass == null || Object.class.equals(superclass)) {
      return null;  //父類為Object或null則查找結(jié)束
    }
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
    if (jdbcHandlerMap != null) {
      return jdbcHandlerMap;
    } else {
      //繼續(xù)遞歸查找父類對(duì)應(yīng)的TypeHandler集合
      return getJdbcHandlerMapForSuperclass(superclass);
    }
  }

TypeHandlerRegistry.getMappingTypeHandler()方法會(huì)根據(jù)指定的TypeHandler類型捣鲸,直接從ALL_TYPE_HANDLERS_MAP集合中查找TypeHandler對(duì)象。TypeHandlerRegistry.getTypeHandler(JdbcType)方法會(huì)根據(jù)指定的JdbcType類型闽坡,從JDBC_TYPE_HANDLER_MAP集合中查找TypeHandler對(duì)象栽惶。這兩個(gè)方法實(shí)現(xiàn)相對(duì)簡單愁溜,代碼就不貼出來了。
最后外厂,除了MyBatis本身提供的TypeHandler實(shí)現(xiàn)冕象,我們也可以添加自定義的TypeHandler
接口實(shí)現(xiàn),添加方式是在mybatisconfig.xml配置文件中的<typeHandlers>節(jié)點(diǎn)下酣衷,添加相應(yīng)的<typeHandler>節(jié)點(diǎn)配置交惯,并指定自定義的TypeHandler接口實(shí)現(xiàn)類。在MyBatis初始化時(shí)會(huì)解析該節(jié)點(diǎn)穿仪,并將該TypeHandler類型的對(duì)象注冊(cè)到TypeHandlerRegistry中席爽,供MyBatis后續(xù)使用。在后面介紹MyBatis初始化時(shí)還會(huì)提到該配置啊片。

TypeAliasRegistry

在編寫SQL語句時(shí)只锻,使用別名可以方便理解以及維護(hù),例如表名或列名很長時(shí)紫谷,我們一般會(huì)為其設(shè)計(jì)易懂易維護(hù)的別名齐饮。MyBatis將SQL語句中別名的概念進(jìn)行了延伸和擴(kuò)展,MyBatis可以為一個(gè)類添加一一個(gè)別名笤昨,之后就可以通過別名引用該類祖驱。MyBatis通過TypeAliasRegistry類完成別名注冊(cè)和管理的功能,TypeAliasRegistry的結(jié)構(gòu)比較簡單,它通過TYPE_ ALIASES字段(Map<String, Class<?>>類型)管理別名與Java類型之間的對(duì)應(yīng)關(guān)系瞒窒,通過TypeAliasRegistry.registerAlias()方法完成注冊(cè)別名捺僻,該方法的具體實(shí)現(xiàn)如下:

public void registerAlias(String alias, Class<?> value) {
  if (alias == null) {  //校驗(yàn)別名是否為空,為空則拋出類型異常
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  //將別名轉(zhuǎn)換為小寫
  String key = alias.toLowerCase(Locale.ENGLISH);
  //校驗(yàn)別名是否已經(jīng)存在
  if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
  }
  //注冊(cè)別名
  typeAliases.put(key, value);
}

TypeAliasRegistry的構(gòu)造方法中崇裁,默認(rèn)為Java的基本類型及其數(shù)組類型匕坯、基本類型的封裝類及其數(shù)組類型、Date拔稳、BigDecimal葛峻、BigIntegerMap巴比、HashMap术奖、ListArrayList轻绞、Collection采记、IteratorResultSet等類型添加了別名铲球,代碼比較簡單挺庞,請(qǐng)讀者參考TypeAliasRegistry的源碼進(jìn)行學(xué)習(xí)。

TypeAliasRegistry中還有兩個(gè)方法需要介紹一下稼病,registerAliases(String,Class<?>)重載會(huì)掃描指定包下所有的類选侨,并為指定類的子類添加別名; registerAlias(Class<?>)重 載中會(huì)嘗試讀取@Alias注解掖鱼。這兩個(gè)方法的實(shí)現(xiàn)如下:

public void registerAliases(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  //查找指定包下的superType類型類
  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
    //過濾調(diào)內(nèi)部類、接口援制、抽象類
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      registerAlias(type);
    }
  }
}

public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName();  //類型的簡單名稱(不包含報(bào)名)
  //讀取Alias注解
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  //檢測次別名不存在后戏挡,會(huì)將其記錄到TYPE_ALIASES集合中
  registerAlias(alias, type);
}

本文由 Janker 創(chuàng)作,采用 CC BY 3.0 CN協(xié)議 進(jìn)行許可晨仑。 可自由轉(zhuǎn)載褐墅、引用,但需署名作者且注明文章出處洪己。如轉(zhuǎn)載至微信公眾號(hào)妥凳,請(qǐng)?jiān)谖哪┨砑幼髡吖娞?hào)二維碼。
<img src="http://qiniu-cdn.janker.top/oneblog/20200311150833864.jpg" style="zoom:50%;" />

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末答捕,一起剝皮案震驚了整個(gè)濱河市逝钥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拱镐,老刑警劉巖艘款,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沃琅,居然都是意外死亡哗咆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門益眉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晌柬,“玉大人,你說我怎么就攤上這事呜叫】辗保” “怎么了殿衰?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵朱庆,是天一觀的道長。 經(jīng)常有香客問我闷祥,道長娱颊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任凯砍,我火速辦了婚禮箱硕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘悟衩。我一直安慰自己剧罩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布座泳。 她就那樣靜靜地躺著惠昔,像睡著了一般幕与。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上镇防,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天啦鸣,我揣著相機(jī)與錄音,去河邊找鬼来氧。 笑死诫给,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啦扬。 我是一名探鬼主播中狂,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扑毡!你這毒婦竟也來了吃型?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤僚楞,失蹤者是張志新(化名)和其女友劉穎勤晚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泉褐,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赐写,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膜赃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挺邀。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跳座,靈堂內(nèi)的尸體忽然破棺而出端铛,到底是詐尸還是另有隱情,我是刑警寧澤疲眷,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布禾蚕,位于F島的核電站,受9級(jí)特大地震影響狂丝,放射性物質(zhì)發(fā)生泄漏换淆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一几颜、第九天 我趴在偏房一處隱蔽的房頂上張望倍试。 院中可真熱鬧,春花似錦蛋哭、人聲如沸县习。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躁愿。三九已至哈蝇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間攘已,已是汗流浹背炮赦。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留样勃,地道東北人吠勘。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像峡眶,于是被迫代替她去往敵國和親剧防。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容