首页 > 编程语言 >【Mybatis】【配置文件解析】【三】Mybatis源码解析-typeHandler、objectWrapperFactory

【Mybatis】【配置文件解析】【三】Mybatis源码解析-typeHandler、objectWrapperFactory

时间:2023-02-26 16:11:45浏览次数:44  
标签:null java 配置文件 typeHandler register javaType jdbcType Mybatis 解析

1  前言

这节我们继续我们的配置文件分析,本节我们看看typeHandler和objectWrapperFactory的解析。

2  源码分析

2.1  解析typeHandlers

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

提示 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。
类型处理器Java 类型JDBC 类型
BooleanTypeHandler java.lang.Booleanboolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Shortshort 数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integerint 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Longlong 数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Floatfloat 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Doubledouble 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOBLONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandler java.lang.String SQLXML
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR 或 LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

你可以重写已有的类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 并且可以(可选地)将它映射到一个 JDBC 类型。

让我们来看下typeHandler的dtd约束:

<!ELEMENT typeHandlers (typeHandler*,package*)>

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

可以看见它分包扫描和配置的方式,包扫描也就是需要我们的注解应使用@MappedTypes@MappedJdbcTypes注解配置javaTypejdbcType。

那么让我们看下源码:

private void typeHandlerElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 包形式的解析
      if ("package".equals(child.getName())) {
        String typeHandlerPackage = child.getStringAttribute("name");
        // 获取到包的值 进行注册
        typeHandlerRegistry.register(typeHandlerPackage);
      } else {
        /**
         * typeHandler 子标签的形式解析
         * 解析出 javaType jdbcType handler的值
         */
        String javaTypeName = child.getStringAttribute("javaType");
        String jdbcTypeName = child.getStringAttribute("jdbcType");
        String handlerTypeName = child.getStringAttribute("handler");
        // 转换成对应的类型
        Class<?> javaTypeClass = resolveClass(javaTypeName);
        JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
        Class<?> typeHandlerClass = resolveClass(handlerTypeName);
        // 根据 javaTypeClass 和 jdbcType 值的情况进行不同的注册策略
        if (javaTypeClass != null) {
          if (jdbcType == null) {
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
          } else {
            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
          }
        } else {
          typeHandlerRegistry.register(typeHandlerClass);
        }
      }
    }
  }
}

可以看到 typeHandlerRegistry 重载的方法就有4个里边还有互相调用衍生更多重载,一个个看进去迷糊了调来调去的画个图理一下:

我们就按图上的1、2、3、4进行一一的分析哈。

2.1.1  register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass)方法分析

当代码执行到此方法时,表示javaTypeClass != null && jdbcType != null条件成立,即使用者明确配置了javaTypejdbcType属性的值。那下面我们来看一下该方法的分析。

public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
  // 调用终点方法
  register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
}
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
  // 继续调用重载
  register((Type) type, jdbcType, handler);
}
// 类型处理器注册过程的终点
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
    // JdbcType 到 TypeHandler 的映射
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      map = new HashMap<>();
    }
    map.put(jdbcType, handler);
    // 存储 javaType 到 Map<JdbcType, TypeHandler> 的映射
    typeHandlerMap.put(javaType, map);
  }
  // 存储所有的 TypeHandler
  allTypeHandlersMap.put(handler.getClass(), handler);
}

上面的代码经过三层调用调用,比较简单。同时,所谓的注册过程也就是把类型和处理器进行映射而已,没什么特别之处。关于这个方法就先分析到这里,继续往下分析。

2.1.2 register(Class<?> javaTypeClass, Class<?> typeHandlerClass)方法分析

当代码执行到此方法时,表示javaTypeClass != null && jdbcType == null条件成立,即使用者仅设置了javaType属性的值。下面我们来看一下该方法的分析。

public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  // 调用重载方法
  register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
  // 继续调用重载方法
  register((Type) javaType, typeHandler);
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  // 获取 @MappedJdbcTypes 注解
  MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  if (mappedJdbcTypes != null) {
    // 遍历 @MappedJdbcTypes 注解中配置的值
    for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
      // 调用终点方法
      register(javaType, handledJdbcType, typeHandler);
    }
    if (mappedJdbcTypes.includeNullJdbcType()) {
      // 调用终点方法,jdbcType = null
      register(javaType, null, typeHandler);
    }
  } else {
    // 调用终点方法,jdbcType = null
    register(javaType, null, typeHandler);
  }

上面的代码包含三层调用,其中终点方法的逻辑上一节已经分析过,这里不再赘述。上面的逻辑也比较简单,主要做的事情是尝试从注解中获取JdbcType的值。这个方法就分析这么多,继续。

2.1.3 register(Class<?> typeHandlerClass)方法分析

当代码执行到此方法时,表示javaTypeClass == null && jdbcType != null条件成立,即使用者未配置javaTypejdbcType属性的值。该方法的分析如下。

public void register(Class<?> typeHandlerClass) {
  boolean mappedTypeFound = false;
  // 获取 @MappedTypes 注解
  MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
  if (mappedTypes != null) {
    // 遍历 @MappedTypes 注解中配置的值
    for (Class<?> javaTypeClass : mappedTypes.value()) {
      // 调用上面的第2个注册方法
      register(javaTypeClass, typeHandlerClass);
      mappedTypeFound = true;
    }
  }
  if (!mappedTypeFound) {
    // 调用中间方法 register(TypeHandler)
    register(getInstance(null, typeHandlerClass));
  }
}
public <T> void register(TypeHandler<T> typeHandler) {
  boolean mappedTypeFound = false;
  // 获取 @MappedTypes 注解
  MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
  if (mappedTypes != null) {
    for (Class<?> handledType : mappedTypes.value()) {
      // 调用中间方法 register(Type, TypeHandler)
      register(handledType, typeHandler);
      mappedTypeFound = true;
    }
  }
  // 自动发现映射类型
  // @since 3.1.0 - try to auto-discover the mapped type
  if (!mappedTypeFound && typeHandler instanceof TypeReference) {
    try {
      TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
      // 获取参数模板中的参数类型,并调用中间方法 register(Type, 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, TypeHandler)
    register((Class<T>) null, typeHandler);
  }
}

上面的代码比较多,不过不用太担心。不管是通过注解的方式,还是通过反射的方式,它们最终目的是为了解析出javaType的值。解析完成后,这些方法会调用中间方法register(Type, TypeHandler),这个方法负责解析jdbcType,该方法上一节已经分析过。一个负责解析 javaType,另一个负责解析 jdbcType,继续最后一个。

2.1.4 register(StringpackageName)方法分析

该方法主要是用于自动扫描类型处理器,并调用其他方法注册扫描结果,我们看源码:

public void register(String packageName) {
  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())) {
      // 调用上边第3个注册方法
      register(type);
    }
  }
}

方法重载的多不,中间都是为了解析出数据值,最后都会划归到javaType、jdbcType、typeHandler的方法里,是不是。

2.2 解析objectWrapperFactory

关于objectWrapperFactory的解析也比较简单哈,我们简单看一下dtd约束和他的解析源码:

<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>
private void objectWrapperFactoryElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");
    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    configuration.setObjectWrapperFactory(factory);
  }
}

3  小结

好了本节我们解析了typeHandler和objectWrapperFactory,我们还剩下一个很重要的,我单独拿一节来进行分析哈,有理解不对的地方欢迎指正哈。

标签:null,java,配置文件,typeHandler,register,javaType,jdbcType,Mybatis,解析
From: https://www.cnblogs.com/kukuxjx/p/17156684.html

相关文章