0

0

深入浅出Mybatis系列(五)---TypeHandler简介及配置(mybatis源码篇)

黄舟

黄舟

发布时间:2017-03-02 10:48:09

|

1847人浏览过

|

来源于php中文网

原创

上篇文章《深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)》为大家介绍了mybatis中别名的使用,以及其源码。本篇将为大家介绍TypeHandler, 并简单分析其源码。

Mybatis中的TypeHandler是什么?

  无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。

那么,Mybatis为我们实现了哪些TypeHandler呢?  我们怎么自定义实现一个TypeHandler ?  这些都会在接下来的mybatis的源码中看到。

在看源码之前,还是像之前一样,先看看怎么配置吧?

配置TypeHandler:


    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
  
  
  ......  

上面简单介绍了一下TypeHandler,  下面就看看mybatis中TypeHandler的源码了。


=========================================================我是源码分割线==========================================================

老规矩,先从对xml的解析讲起:


/**
 * 解析typeHandlers节点 */private void typeHandlerElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        //子节点为package时,获取其name属性的值,然后自动扫描package下的自定义typeHandler
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {          //子节点为typeHandler时, 可以指定javaType属性, 也可以指定jdbcType, 也可两者都指定          //javaType 是指定java类型          //jdbcType 是指定jdbc类型(数据库类型: 如varchar)
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");          //handler就是我们配置的typeHandler
          String handlerTypeName = child.getStringAttribute("handler");          //resolveClass方法就是我们上篇文章所讲的TypeAliasRegistry里面处理别名的方法
          Class javaTypeClass = resolveClass(javaTypeName);          //JdbcType是一个枚举类型,resolveJdbcType方法是在获取枚举类型的值
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class typeHandlerClass = resolveClass(handlerTypeName);          //注册typeHandler, typeHandler通过TypeHandlerRegistry这个类管理
          if (javaTypeClass != null) {            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
}


接下来看看TypeHandler的管理注册类:

TypeHandlerRegistry:

/**
 * typeHandler注册管理类 */public final class TypeHandlerRegistry {  
 //源码一上来,二话不说,几个大大的HashMap就出现,这不又跟上次讲的typeAliases的注册类似么  
 //基本数据类型与其包装类
  private static final Map, Class> reversePrimitiveMap = new HashMap, Class>() {    private static final long serialVersionUID = 1L;
    {
      put(Byte.class, byte.class);
      put(Short.class, short.class);
      put(Integer.class, int.class);
      put(Long.class, long.class);
      put(Float.class, float.class);
      put(Double.class, double.class);
      put(Boolean.class, boolean.class);
      put(Character.class, char.class);
    }
  };  //这几个MAP不用说就知道存的是什么东西吧,命名的好处
  private final Map> JDBC_TYPE_HANDLER_MAP = new EnumMap>(JdbcType.class);  private final Map>> TYPE_HANDLER_MAP = new HashMap>>();  private final TypeHandler UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);  private final Map, TypeHandler> ALL_TYPE_HANDLERS_MAP = new HashMap, TypeHandler>();  //就像上篇文章讲的typeAliases一样,mybatis也默认给我们注册了不少的typeHandler  //具体如下
  public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }  public boolean hasTypeHandler(Class javaType) {    return hasTypeHandler(javaType, null);
  }  public boolean hasTypeHandler(TypeReference javaTypeReference) {    return hasTypeHandler(javaTypeReference, null);
  }  public boolean hasTypeHandler(Class javaType, JdbcType jdbcType) {    return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
  }  public boolean hasTypeHandler(TypeReference javaTypeReference, JdbcType jdbcType) {    return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
  }  public TypeHandler getMappingTypeHandler(Class> handlerType) {    return ALL_TYPE_HANDLERS_MAP.get(handlerType);
  }  public  TypeHandler getTypeHandler(Class type) {    return getTypeHandler((Type) type, null);
  }  public  TypeHandler getTypeHandler(TypeReference javaTypeReference) {    return getTypeHandler(javaTypeReference, null);
  }  public TypeHandler getTypeHandler(JdbcType jdbcType) {    return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
  }  public  TypeHandler getTypeHandler(Class type, JdbcType jdbcType) {    return getTypeHandler((Type) type, jdbcType);
  }  public  TypeHandler getTypeHandler(TypeReference javaTypeReference, JdbcType jdbcType) {    return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
  }  private  TypeHandler getTypeHandler(Type type, JdbcType jdbcType) {
    Map> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
    TypeHandler handler = null;    if (jdbcHandlerMap != null) {
      handler = jdbcHandlerMap.get(jdbcType);      if (handler == null) {
        handler = jdbcHandlerMap.get(null);
      }
    }    if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class) type)) {
      handler = new EnumTypeHandler((Class) type);
    }
    @SuppressWarnings("unchecked")    // type drives generics here
    TypeHandler returned = (TypeHandler) handler;    return returned;
  }  public TypeHandler getUnknownTypeHandler() {    return UNKNOWN_TYPE_HANDLER;
  }  public void register(JdbcType jdbcType, TypeHandler handler) {
    JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
  }  //
  // REGISTER INSTANCE  
  //
  /**
   * 只配置了typeHandler, 没有配置jdbcType 或者javaType   
   */
  @SuppressWarnings("unchecked")  public  void register(TypeHandler typeHandler) {    
  boolean mappedTypeFound = false;    
  //在自定义typeHandler的时候,可以加上注解MappedTypes 去指定关联的javaType    
  //因此,此处需要扫描MappedTypes注解
    MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);    
    if (mappedTypes != null) {      
    for (Class handledType : mappedTypes.value()) {
        register(handledType, typeHandler);
        mappedTypeFound = true;
      }
    }    // @since 3.1.0 - try to auto-discover the mapped type
    if (!mappedTypeFound && typeHandler instanceof TypeReference) {      try {
        TypeReference typeReference = (TypeReference) typeHandler;
        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) {
      register((Class) null, typeHandler);
    }
  }  /**
   * 配置了typeHandlerhe和javaType   */
  public  void register(Class javaType, TypeHandler typeHandler) {
    register((Type) javaType, typeHandler);
  }  private  void register(Type javaType, TypeHandler typeHandler) {    
  //扫描注解MappedJdbcTypes
    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  void register(TypeReference javaTypeReference, TypeHandler handler) {
    register(javaTypeReference.getRawType(), handler);
  }  /**
   * typeHandlerhe、javaType、jdbcType都配置了   */
  public  void register(Class type, JdbcType jdbcType, TypeHandler handler) {
    register((Type) type, jdbcType, handler);
  }  /**
   * 注册typeHandler的核心方法
   * 就是向Map新增数据而已   */
  private void register(Type javaType, JdbcType jdbcType, TypeHandler handler) {    
  if (javaType != null) {
      Map> map = TYPE_HANDLER_MAP.get(javaType);      
      if (map == null) {
        map = new HashMap>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);      
      if (reversePrimitiveMap.containsKey(javaType)) {
        register(reversePrimitiveMap.get(javaType), jdbcType, handler);
      }
    }
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }  //
  // REGISTER CLASS  //

  // Only handler type

  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));
    }
  }  // java type + handler type

  public void register(Class javaTypeClass, Class typeHandlerClass) {
    register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  }  // java type + jdbc type + handler type

  public void register(Class javaTypeClass, JdbcType jdbcType, Class typeHandlerClass) {
    register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
  }  // Construct a handler (used also from Builders)
  @SuppressWarnings("unchecked")  public  TypeHandler getInstance(Class javaTypeClass, Class typeHandlerClass) {    if (javaTypeClass != null) {      try {
        Constructor c = typeHandlerClass.getConstructor(Class.class);        
        return (TypeHandler) c.newInstance(javaTypeClass);
      } catch (NoSuchMethodException ignored) {        
      // ignored
      } catch (Exception e) {        
      throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }    try {
      Constructor c = typeHandlerClass.getConstructor();      
      return (TypeHandler) c.newInstance();
    } catch (Exception e) {     
     throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  } 
  /**
   * 根据指定的pacakge去扫描自定义的typeHander,然后注册   */
  public void register(String packageName) {
    ResolverUtil> resolverUtil = new ResolverUtil>();
    resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
    Set>> 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);
      }
    }
  }  
  // get information
  
  /**
   * 通过configuration对象可以获取已注册的所有typeHandler   */
  public Collection> getTypeHandlers() {    
  return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
  }
  
}


由源码可以看到, mybatis为我们实现了那么多TypeHandler,  随便打开一个TypeHandler,看其源码,都可以看到,它继承自一个抽象类:BaseTypeHandler, 那么我们是不是也能通过继承BaseTypeHandler,从而实现自定义的TypeHandler ? 答案是肯定的, 那么现在下面就为大家演示一下自定义TypeHandler:

=====================================================自定义TypeHandler分割线============================================================

ExampleTypeHandler:

@MappedJdbcTypes(JdbcType.VARCHAR)  
//此处如果不用注解指定jdbcType, 那么,就可以在配置文件中通过"jdbcType"属性指定, 同理, javaType 也可通过 @MappedTypes指定public class ExampleTypeHandler extends BaseTypeHandler {

  @Override  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {    return rs.getString(columnName);
  }

  @Override  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {    return rs.getString(columnIndex);
  }

  @Override  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {    return cs.getString(columnIndex);
  }
}


然后,就该配置我们的自定义TypeHandler了:



  
      
      
  
  
  ......  


也就是说,我们在自定义TypeHandler的时候,可以在TypeHandler通过@MappedJdbcTypes指定jdbcType, 通过 @MappedTypes 指定javaType, 如果没有使用注解指定,那么我们就需要在配置文件中配置。

好啦,本篇文章到此结束。

 以上就是深入浅出mybatis系列(五)---typehandler简介及配置(mybatis源码篇)的内容,更多相关内容请关注php中文网(www.php.cn)!

RecoveryFox AI
RecoveryFox AI

AI驱动的数据恢复、文件恢复工具

下载


热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

178

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

35

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

79

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

2

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

4

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

8

2026.01.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

24

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

122

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

72

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2万人学习

Webpack4.x---十天技能课堂
Webpack4.x---十天技能课堂

共20课时 | 1.4万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 812人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号