Mybatis(四): 类型转换器模块详解

1,817 阅读11分钟

mybatis.jpg [toc]

在上篇文章中我们介绍了下解析器模块的源码,在今天的文章中我们介绍下Mybatis的另一个基础模块-解析器模块。

类型转换器的作用是负责Java类和jdbc类型之间的转换。

该模块对应源码中的org.apache.ibatis.type包,这个里面的类挺多,但是别慌,大部分类都是TypeHandler的子类,只是负责处理不同的类型罢了,在本篇文章中我们介绍的类如下:

  • TypeHandler 类型转换器的顶层接口
  • BaseTypeHandler 抽象类继承自TypeHandlerMybatis中所有的类型转换器实现均继承他。
  • TypeHandlerRegistry 类型转换器注册器,负责存储类型转换器。
  • TypeAliasRegistry 类型别名转换器,用来存储类型与别名的对应关系。

1 TypeHandler

TypeHandler是类型转换器的顶层接口,其定义了类型转换器应该具有的功能,其源码如下:

public interface TypeHandler<T> {
    // 为sql语句绑定参数  操作PreparedStament
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
​
    // 获取数据  操作ResultSet
    T getResult(ResultSet rs, String columnName) throws SQLException;
​
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
​
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;
​
}

2 BaseTypeHandler

BaseTypeHandler是一个抽象类,改类实现了TypeHandler中的方法并实现了异常捕获。继承改类我们可以很容易的实现一个自定义类型转换器,其源码如下:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
​
    @Deprecated
    protected Configuration configuration;
​
    @Deprecated
    public void setConfiguration(Configuration c) {
        this.configuration = c;
    }
​
    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
            if (jdbcType == null) {
                throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
            }
            try {
                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 {
                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);
            }
        }
    }
​
    @Override
    public T getResult(ResultSet rs, String columnName) throws SQLException {
        try {
            return getNullableResult(rs, columnName);
        } catch (Exception e) {
            throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
        }
    }
​
    @Override
    public T getResult(ResultSet rs, int columnIndex) throws SQLException {
        try {
            return getNullableResult(rs, columnIndex);
        } catch (Exception e) {
            throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set.  Cause: " + e, e);
        }
    }
​
    @Override
    public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
        try {
            return getNullableResult(cs, columnIndex);
        } catch (Exception e) {
            throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement.  Cause: " + e, e);
        }
    }
​
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
​
    public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
​
    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
​
    public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
​
}

3 自定义类型转换器

在上面我们介绍了类型转换器的父接口,接下来我们演示下当我们需要定义类型转换器时应该如何操作。

该代码已提交至github 有需要的可以自取:github.com/xiehuaa/myb…

3.1 功能介绍

实现的功能也比较简单哈,目前有张employee表,其数据如下:

image-20210831120151963

对于role字段,我想添加时传递List对象,获取时也自动转换为List

3.2 编写类型转换器

我们定义一个RoleTypeHandler继承BaseTypeHandler,其源码如下

/**
 * Create By IntelliJ IDEA
 *
 * @author: XieHua
 * @date: 2021-08-30 20:25
 */
public class RoleTypeHandler extends BaseTypeHandler<List<Integer>> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<Integer> parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null || parameter.isEmpty()) {
            ps.setString(i, "");
        } else {
            StringBuilder paramStr = new StringBuilder();
            for (int j = 0; j < parameter.size(); j ++) {
                paramStr.append(parameter.get(j));
                if (j != parameter.size() - 1) {
                    paramStr.append(",");
                }
            }
            ps.setString(i, paramStr.toString());
        }
    }
​
    @Override
    public List<Integer> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.convertToList(rs.getString(columnName));
    }
​
    @Override
    public List<Integer> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.convertToList(rs.getString(columnIndex));
    }
​
    @Override
    public List<Integer> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.convertToList(cs.getString(columnIndex));
    }
​
    // 字符串转换为list
    private List<Integer> convertToList(String rolesStr) {
        List<Integer> roleList = new ArrayList<>();
        if (!"".equals(rolesStr)) {
            String[] roleIdStrArray = rolesStr.split(",");
            for (String roleIdStr : roleIdStrArray) {
                roleList.add(Integer.parseInt(roleIdStr));
            }
        }
​
        return roleList;
    }
}

3.3 实体及Mapper如下

/**
 * Create By IntelliJ IDEA
 *
 * @author: XieHua
 * @date: 2021-08-30 20:23
 */
@Setter
@Getter
public class Employee {
    private Integer id;
    private String name;
    private List<Integer> role;
}
​
/**
 * Create By IntelliJ IDEA
 *
 * @author: XieHua
 * @date: 2021-08-02 15:05
 */
public interface EmployeeMapper {
​
    int insert(@Param("employee") Employee employee);
​
    Employee getById(@Param("id") Integer id);
}

3.3 配置文件

在配置文件中加入该类型转换器,增加如下代码:

<typeHandlers>
    <typeHandler handler="com.xh.sample.mybatis.type.RoleTypeHandler"/>
</typeHandlers>

Mapper映射文件如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xh.sample.mybatis.mapper.EmployeeMapper">
    <resultMap id="BaseResultMap" type="com.xh.sample.mybatis.entity.Employee">
        <result column="id" property="id" jdbcType="INTEGER"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <!-- 设置为自定义的类型转换器 -->
        <result column="role" property="role" typeHandler="com.xh.sample.mybatis.type.RoleTypeHandler"/>
    </resultMap>
    <insert id="insert">
        INSERT INTO employee (name, role)
         VALUES
          (#{employee.name},
        <!-- 设置为自定义的类型转换器 -->
           #{employee.role,jdbcType=VARCHAR,typeHandler=com.xh.sample.mybatis.type.RoleTypeHandler})
    </insert>
​
    <select id="getById" resultMap="BaseResultMap">
        SELECT * FROM employee WHERE id = #{id}
    </select>
</mapper>

3.4 测试

测试代码如下:

public static void main(String[] args) throws IOException {
    // 配置文件路径
    String resource = "mybatis.xml";
    // 加载配置文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 创建SqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 获取SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
​
    // insertEmployee(sqlSession);
    getEmployee(sqlSession);
}
​
private static void insertEmployee (SqlSession sqlSession) {
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee employee = new Employee();
    employee.setName("张三");
    employee.setRole(Arrays.asList(1, 2));
    employeeMapper.insert(employee);
    // 提交事务
    sqlSession.commit();
    sqlSession.close();
}
​
private static void getEmployee(SqlSession sqlSession) {
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee employee = employeeMapper.getById(2);
    System.out.println(JSON.toJSONString(employee));
​
    sqlSession.close();
}

4 TypeHandlerRegistry

了解了TypeHandler后,我们学习下Mybatis是如何对TypeHandler进行管理的。在TypeHandlerRegistry中定义了存储TypeHandler的容器及注册和获取的方法。 其属性如下:

// jdbc类型和TypeHandler的关系 
private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
// java类型和jdbc类型的关系   可以通过javaType和jdbcType获取到对应的TypeHandler   一个java类型会对应多个jdbc类型
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
// UnknowTypeHandler对象   
private final TypeHandler<Object> unknownTypeHandler;
// 存储了TypeHandler类型与其对象的关系
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
// 空TypeHandler
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
​
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

他的构造方法这里就不粘贴,构造方法中的逻辑是将Mybatis已知的类型转换器进行注册,大家自行查看吧,我们会在下面介绍下注册方法。

4.1 注册类型转换器

TypeHandlerRegistry中提供了一系列的register方法,这些方法用于类型转换器的注册,这些方法的定义如下:

register(JdbcType jdbcType, TypeHandler<?> handler);
​
public <T> void register(TypeHandler<T> typeHandler);
​
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler);
​
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler);
​
public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler);
​
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler);
​
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler);
​
public void register(Class<?> typeHandlerClass);
​
public void register(String javaTypeClassName, String typeHandlerClassName);
​
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass);
​
public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass);
​
public void register(String packageName);

为了便于梳理这些方法的调用关系,我画了个这些方法之间的调用关系图如下:

image-20210831180848764

处于调用关系最底层的是register(JdbcType jdbcType, TypeHandler<?> handler)register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)方法,我们先看看这两个方法的逻辑,如下:

public void register(JdbcType jdbcType, TypeHandler<?> handler) {
    // 向jdbcTypeHandlerMap中存入数据   记录jdbcType对应的TypeHandler对象
    jdbcTypeHandlerMap.put(jdbcType, handler);
}
​
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
        // 获取java类型对应的typeHandler
        Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
        if (map == null || map == NULL_TYPE_HANDLER_MAP) {
            map = new HashMap<>();
        }
        // 添加到map中
        map.put(jdbcType, handler);
        // 向typeHandlerMap中添加数据
        typeHandlerMap.put(javaType, map);
    }
    // 维护typeHandler类型与其对应关系
    allTypeHandlersMap.put(handler.getClass(), handler);
}

通过源码可以看到,这两个底层的方法就是操作TypeHandlerRegistry中的属性字段。再往上一层的方法有register(Type javaType, TypeHandler<? extends T> typeHandler)register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler)。这两个方法的源码如下:

private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    // 获取TypeHandler上的MappedJdbcTypes注解   该注解会表示这个类型转换器能够处理的jdbc类型
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
        for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
            register(javaType, handledJdbcType, typeHandler);
        }
        if (mappedJdbcTypes.includeNullJdbcType()) {
            register(javaType, null, typeHandler);
        }
    } else {
        register(javaType, null, typeHandler);
    }
}
​
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
    register((Type) type, jdbcType, handler);
}

再往上面的方法没有什么逻辑,就是直接调用下层的重载方法,这里我们看看register(TypeHandler<T> typeHandler)register(Class<?> typeHandlerClass)register(String packageName)三个方法。

public <T> void register(TypeHandler<T> typeHandler) {
    boolean mappedTypeFound = false;
    // 获取TypeHandler类上的MappedTypes注解   该注解用于表示该TypeHandler适用的Java类型
    MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
    // 如果注解存在
    if (mappedTypes != null) {
        // 遍历value值   获取到使用的java类型
        for (Class<?> handledType : mappedTypes.value()) {
            // 进行注册
            register(handledType, typeHandler);
            mappedTypeFound = true;
        }
    }
    // MappedTypes注册不存在或者没有配置对应的属性   且   typeHandler属于TypeReference
    if (!mappedTypeFound && typeHandler instanceof TypeReference) {
        try {
            TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
            // 注册类型转换器   typeReference.getRawType获取到的是java类型
            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) {
        // 这个逻辑会操作allTypeHandlersMap
        register((Class<T>) null, typeHandler);
    }
}
public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
        for (Class<?> javaTypeClass : mappedTypes.value()) {
            register(javaTypeClass, typeHandlerClass);
            mappedTypeFound = true;
        }
    }
    if (!mappedTypeFound) {
        register(getInstance(null, typeHandlerClass));
    }
}
public void register(String packageName) {
    // Mybatis中io工具
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 找到这个类包路径下的父类为TypeHandler的类
    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
        if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
            // 注册类型转换器
            register(type);
        }
    }
}

4.2 查找类型转换器

TypeHandlerRegistry中获取类型转换器的方法也有多个方法,这里我们可以将其划分为三类

  • 通过类型转换器类型查找
  • 通过Jdbc类型查找
  • 通过Java类型的查找

4.2.1 通过类型转换器类型查找

通过类型转换器类型查找对应的对象的方法是getMappingTypeHandler,该方法比较简单,直接从allTypehandlersMap中进行查找,其源码如下:

public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
    return allTypeHandlersMap.get(handlerType);
}

4.2.2 通过JdbcType查找

通过JdbcType查找对应的类型转换器方法是getTypeHandler(JdbcType jdbcType),这个方法也没什么逻辑,直接从jdbcTypeHandlerMap中进行查找,其源码如下:

public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
    return jdbcTypeHandlerMap.get(jdbcType);
}

4.2.3 通过Java类型查找

通过Java类型查找的方法有几个,但他们最终都调用到getTypeHandler(Type type, JdbcType jdbcType)方法,该方法的逻辑如下:

private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
    if (ParamMap.class.equals(type)) {
        return null;
    }
    // 获取Java类型对应的类型转换器集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
    TypeHandler<?> handler = null;
    if (jdbcHandlerMap != null) {
        // 再根据JdbcType查询对应的类型转换器
        handler = jdbcHandlerMap.get(jdbcType);
        // 没找到
        if (handler == null) {
            // 获取null对应的类型转换器
            handler = jdbcHandlerMap.get(null);
        }
        if (handler == null) {
            // #591
            // 如果只注册了一个类型转换器则使用该类型转换器
            handler = pickSoleHandler(jdbcHandlerMap);
        }
    }
    // type drives generics here
    return (TypeHandler<T>) handler;
}

通过Java类型查找对应的类型转换器列表的逻辑如下:

private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
    // 从typeHandlerMap中获取java类型对应的类型转换器集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
    // 如果是空直接返回null
    if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
        return null;
    }
    // 类型转换器列表为空
    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 {
            // 查找父类的类型转换器集合
            jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
        }
    }
    // 存入到typeHandlerMap中
    typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
    // 返回类型转换器集合
    return jdbcHandlerMap;
}
​
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
    // 遍历实现的接口
    for (Class<?> iface : clazz.getInterfaces()) {
        // 查询对应的类型转换器列表
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(iface);
        if (jdbcHandlerMap == null) {
            // 递归查找父接口
            jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
        }
        // 父接口存在对应的类型转换器
        if (jdbcHandlerMap != null) {
            // Found a type handler registered to a super interface
            // 新建一个map 存储Java类型对应的类型转换器
            HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
            // 遍历父接口的类型转换器存储到新的map中
            for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
                // Create a type handler instance with enum type as a constructor arg
                newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
            }
            // 返回
            return newMap;
        }
    }
    // 返回null
    return null;
}
​
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
    // 获取改类的父类
    Class<?> superclass =  clazz.getSuperclass();
    // 父类为Object或者为null
    if (superclass == null || Object.class.equals(superclass)) {
        return null;
    }
    // 从typeHandlerMap中查找对应的类型转换器
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
    if (jdbcHandlerMap != null) {
        // 存在  直接返回
        return jdbcHandlerMap;
    } else {
        // 递归查询父类
        return getJdbcHandlerMapForSuperclass(superclass);
    }
}

5 TypeAliasRegistry

Mybatis中提供了对别名的支持,我们可以为一个类指定别名,然后通过这个别名来引用这个类。TypeAliasRegistry的作用便是用来管理类的别名的。

以我们自定义的类型转换器为例,如果我们想要使用别名,配置可以修改成如下:

<typeAliases>
    <typeAlias type="com.xh.sample.mybatis.type.RoleTypeHandler" alias="roleTypeHandler"/>
</typeAliases><typeHandlers>
    <typeHandler handler="RoleTypeHandler"/>
</typeHandlers>

TypeAliasRegistry的类结构比较简单,里面只有一个Map<String, Class<?>> typeAliases = new HashMap<>()属性,这个属性用来存储别名和类型之间的对应关系。在TypeAliasRegistry中提供了注册和通过别名获取类型的方法,接下来我们看看这两部分内容的逻辑

5.1 注册别名

TypeAliasRegistry中提供了如下几个注册方法

  • registerAliases(String packageName) 注册包下的类
  • registerAliases(String packageName, Class<?> superType) 注册包下指定父类的类
  • registerAlias(Class<?> type) 注册某个类
  • registerAlias(String alias, Class<?> value) 指定别名的方式注册某个类
  • registerAlias(String alias, String value) 通过别名和类全名注册

这些方法的逻辑都比较简单,源码如下:

public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
}
​
public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 查找父类是某个类的类
    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注解
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
        // 获取注解的value属性
        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 (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() + "'.");
    }
    // 添加到map中
    typeAliases.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);
    }
}

5.2 通过别名获取对应的类

TypeAliasRegistry.resolveAlias用于获取别名对应的类型,其源码如下:

public <T> Class<T> resolveAlias(String string) {
    try {
        // 别名为空直接返回null
        if (string == null) {
            return null;
        }
        // issue #748
        // 转为小写
        String key = string.toLowerCase(Locale.ENGLISH);
        Class<T> value;
        // 该别名已经注册
        if (typeAliases.containsKey(key)) {
            // 获取对应的类型
            value = (Class<T>) typeAliases.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);
    }
}

6 总结

至此我们今天的文章就结束了,在本篇文章中我们介绍了Mybatis的类型转换器模块,这个模块是不是也很简单呢?希望大家能通过本文掌握这个模块的工作原理及如何自定义类型转换器。

主要涉及的类如下:

  • TypeHandler 类型转换器顶层接口
  • BaseTypeHandler 一个抽象类,可以简化我们自定义类型转换器
  • TypeHandlerRegistry 类型转换器注册器 用于管理项目中的类型转换器,提供了注册和获取类型转换器的方法
  • TypeAliasRegistry 类型别名注册器 用于管理项目中的别名
  • @MappedTypes 这个注解用于指定一个类转换器可以使用的Java类型
  • @MappedJdbcTypes 这个注解用户指定一个类型转换器可以使用的Jdbc类型
  • @Alias 可以通过这个注解为类提供别名

感谢您的阅读,如果感觉对您有所帮助,欢迎关注本人公众号"Bug搬运小能手",或者扫描下面二维码进行关注

qrcode_for_gh_8febd60b14c9_258.jpg