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)用PreparedStatement
和ResultSet
或CallableStatement
的對(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è)TypeHandler
的register()
重載,還可以掃描整個(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
葛峻、BigInteger
、Map
巴比、HashMap
术奖、List
、ArrayList
轻绞、Collection
采记、Iterator
、ResultSet
等類型添加了別名铲球,代碼比較簡單挺庞,請(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%;" />