美文网首页
MyBatis印象阅读之Configuration、TypeHa

MyBatis印象阅读之Configuration、TypeHa

作者: 向光奔跑_ | 来源:发表于2019-07-30 11:35 被阅读0次

    今天是个适合还债的日子,我们细数下我们还欠下了哪些债:

    • Configuration
    • TypeAliasRegistry
    • TypeHandlerRegistry

    在上章中我们看到使用了Configuration和TypeHandlerRegistry,顾我们今天拿这两个开刀,另外一个TypeAliasRegistry其实也跟TypeHandlerRegistry差不多。

    1. Configuration源码分析

    首先我们得了解这个类是干嘛的?看这单词意思我们就知道是配置管理的,它负责了我们MyBatis运行中整个的配置管理,贯穿了整个使用。

    首先来看属性,反正就是对我们有用的、没用的属性一堆,大家看过一眼就行,实际运行时我们可以拿来再看。

    protected Environment environment;
    
      protected boolean safeRowBoundsEnabled;
      protected boolean safeResultHandlerEnabled = true;
      protected boolean mapUnderscoreToCamelCase;
      protected boolean aggressiveLazyLoading;
      protected boolean multipleResultSetsEnabled = true;
      protected boolean useGeneratedKeys;
      protected boolean useColumnLabel = true;
      protected boolean cacheEnabled = true;
      protected boolean callSettersOnNulls;
      protected boolean useActualParamName = true;
      protected boolean returnInstanceForEmptyRow;
    
      protected String logPrefix;
      protected Class<? extends Log> logImpl;
      protected Class<? extends VFS> vfsImpl;
      protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
      protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
      protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
      protected Integer defaultStatementTimeout;
      protected Integer defaultFetchSize;
      protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
      protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
      protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    
      protected Properties variables = new Properties();
      protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
      protected ObjectFactory objectFactory = new DefaultObjectFactory();
      protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    
      protected boolean lazyLoadingEnabled = false;
      protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
    
      protected String databaseId;
      /**
       * Configuration factory class.
       * Used to create Configuration for loading deserialized unread properties.
       *
       * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
       */
      protected Class<?> configurationFactory;
    
      protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
      protected final InterceptorChain interceptorChain = new InterceptorChain();
      protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
      protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
      protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
      protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
          .conflictMessageProducer((savedValue, targetValue) ->
              ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
      protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
      protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
      protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
      protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    
      protected final Set<String> loadedResources = new HashSet<>();
      protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
    
      protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
      protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
      protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
      protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
    
      /*
       * A map holds cache-ref relationship. The key is the namespace that
       * references a cache bound to another namespace and the value is the
       * namespace which the actual cache is bound to.
       */
      protected final Map<String, String> cacheRefMap = new HashMap<>();
    

    多的让人发指,其中有我们熟悉的,也有我们不熟悉的。

    其次来看它的构造方法,这里还是有点货的,我们来看下:

    public Configuration(Environment environment) {
        this();
        this.environment = environment;
      }
    
      public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    
        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        typeAliasRegistry.registerAlias("LRU", LruCache.class);
        typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    
        typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    
        typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    
        typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
        typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
        typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
        typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
        typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
        typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    
        typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
        typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    
        languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
        languageRegistry.register(RawLanguageDriver.class);
      }
    

    关于Configuration的了解,我们其实目前到这差不多了,总结而言就是保存了一系列的配置,当然也还有其他比如构建Executor的方法,但那些目前分析的话也比较让人烦恼,所以我们放到后面来讲,抓住当下,我们先来一起吧typeAliasRegistry解决掉。

    2. typeAliasRegistry源码解析

    我们知道typeAliasRegistry的功能就是注册别名,可以通过别名来实现获取对应类实例,具体我们看下源码:

    我们继续从属性开始入手:

    private final Map<String, Class<?>> typeAliases = new HashMap<>();
    

    你没有看错,就这一个最简单的HashMap。OK,我们继续来看构造方法:

    public TypeAliasRegistry() {
        registerAlias("string", String.class);
    
        registerAlias("byte", Byte.class);
        registerAlias("long", Long.class);
        registerAlias("short", Short.class);
        registerAlias("int", Integer.class);
        registerAlias("integer", Integer.class);
        registerAlias("double", Double.class);
        registerAlias("float", Float.class);
        registerAlias("boolean", Boolean.class);
    
        registerAlias("byte[]", Byte[].class);
        registerAlias("long[]", Long[].class);
        registerAlias("short[]", Short[].class);
        registerAlias("int[]", Integer[].class);
        registerAlias("integer[]", Integer[].class);
        registerAlias("double[]", Double[].class);
        registerAlias("float[]", Float[].class);
        registerAlias("boolean[]", Boolean[].class);
    
        registerAlias("_byte", byte.class);
        registerAlias("_long", long.class);
        registerAlias("_short", short.class);
        registerAlias("_int", int.class);
        registerAlias("_integer", int.class);
        registerAlias("_double", double.class);
        registerAlias("_float", float.class);
        registerAlias("_boolean", boolean.class);
    
        registerAlias("_byte[]", byte[].class);
        registerAlias("_long[]", long[].class);
        registerAlias("_short[]", short[].class);
        registerAlias("_int[]", int[].class);
        registerAlias("_integer[]", int[].class);
        registerAlias("_double[]", double[].class);
        registerAlias("_float[]", float[].class);
        registerAlias("_boolean[]", boolean[].class);
    
        registerAlias("date", Date.class);
        registerAlias("decimal", BigDecimal.class);
        registerAlias("bigdecimal", BigDecimal.class);
        registerAlias("biginteger", BigInteger.class);
        registerAlias("object", Object.class);
    
        registerAlias("date[]", Date[].class);
        registerAlias("decimal[]", BigDecimal[].class);
        registerAlias("bigdecimal[]", BigDecimal[].class);
        registerAlias("biginteger[]", BigInteger[].class);
        registerAlias("object[]", Object[].class);
    
        registerAlias("map", Map.class);
        registerAlias("hashmap", HashMap.class);
        registerAlias("list", List.class);
        registerAlias("arraylist", ArrayList.class);
        registerAlias("collection", Collection.class);
        registerAlias("iterator", Iterator.class);
    
        registerAlias("ResultSet", ResultSet.class);
      }
    

    会给我们预先注册一些别名信息,那么我们自然而然就进入到registerAlias方法。

    2.1 registerAlias方法解析

      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() + "'.");
        }
        typeAliases.put(key, value);
      }
    

    这个方法简不简单,就是最平常的往HashMap中放入元素。除了这个方法外,我们还会重点使用解析的方法resolveAlias,查找对应别名的类型。

    2.2 resolveAlias方法解析

      public <T> Class<T> resolveAlias(String string) {
        try {
          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);
        }
      }
    

    轻松加愉快就把这个类搞定了。我们继续来看下一个类似的TypeHandlerRegistry类解析。

    3.TypeHandlerRegistry源码解析

    这个类相比较而言稍微复杂了点。我们先来看属性:

    
      private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
      private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
      private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this);
      private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
    
      private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
    
      private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
    

    这里的map个数变多了,可能一下子不能理解其关联关系,没事,我们继续往下看,它构造方法的套路差不多,也是会预先注册一些:

    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(Reader.class, new ClobReaderTypeHandler());
        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 StringTypeHandler());
        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 StringTypeHandler());
        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(InputStream.class, new BlobInputStreamTypeHandler());
        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, unknownTypeHandler);
        register(Object.class, JdbcType.OTHER, unknownTypeHandler);
        register(JdbcType.OTHER, unknownTypeHandler);
    
        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());
    
        register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
    
        register(Instant.class, new InstantTypeHandler());
        register(LocalDateTime.class, new LocalDateTimeTypeHandler());
        register(LocalDate.class, new LocalDateTypeHandler());
        register(LocalTime.class, new LocalTimeTypeHandler());
        register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
        register(OffsetTime.class, new OffsetTimeTypeHandler());
        register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
        register(Month.class, new MonthTypeHandler());
        register(Year.class, new YearTypeHandler());
        register(YearMonth.class, new YearMonthTypeHandler());
        register(JapaneseDate.class, new JapaneseDateTypeHandler());
    
        // issue #273
        register(Character.class, new CharacterTypeHandler());
        register(char.class, new CharacterTypeHandler());
      }
    

    我们又可以进入register方法来进行查看。

    3.1 register方法解析

    我们来看下它是如何来关联关系的:

    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 = 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);
        }
      }
    
      private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        if (javaType != null) {
          Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
          if (map == null || map == NULL_TYPE_HANDLER_MAP) {
            map = new HashMap<>();
            typeHandlerMap.put(javaType, map);
          }
          map.put(jdbcType, handler);
        }
        allTypeHandlersMap.put(handler.getClass(), handler);
      }
    

    这块代码很清晰,这让我们看到了3个map中分别存储的信息。最后就是解析了getTypeHandler方法。

    3.2 getTypeHandler方法解析

    最简单的就是传过来JdbcType,我们直接取:

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

    复杂的话就比较长了,大家可以看下,首先是:

     private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
        if (ParamMap.class.equals(type)) {
          return null;
        }
        //<1>
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null) {
          handler = jdbcHandlerMap.get(jdbcType);
          if (handler == null) {
            handler = jdbcHandlerMap.get(null);
          }
          if (handler == null) {
            // #591
            //<2>
            handler = pickSoleHandler(jdbcHandlerMap);
          }
        }
        // type drives generics here
        return (TypeHandler<T>) handler;
      }
    

    我们先来看<1>标注的方法getJdbcHandlerMap:

     private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
        if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
          return null;
        }
        if (jdbcHandlerMap == null && type instanceof Class) {
          Class<?> clazz = (Class<?>) type;
          if (Enum.class.isAssignableFrom(clazz)) {
            // 继承了Enum的处理方式
            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.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
        return jdbcHandlerMap;
      }
    

    简单总结就是

    • 如果type是enum的继承类,则通过getJdbcHandlerMapForEnumInterfaces进行查询处理,如果没有则使用默认的defaultEnumTypeHandler = EnumTypeHandler.class类,这是针对枚举的
    • 其他情况的话就是查询他父类的类型处理器

    我们看完了<1> ,再来看<2>的pickSoleHandler方法:

      private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
        TypeHandler<?> soleHandler = null;
        for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
          if (soleHandler == null) {
            soleHandler = handler;
          } else if (!handler.getClass().equals(soleHandler.getClass())) {
            // More than one type handlers registered.
            return null;
          }
        }
        return soleHandler;
      }
    

    这个类就是选择的类处理器必须类型是相同的,不同即出现歧义的话,直接返回null。

    4. 今日总结

    今天我们还完了之前欠下的所有债,虽然分析的不是很全面,但是我们可以大致了解了他们的原理。

    相关文章

      网友评论

          本文标题:MyBatis印象阅读之Configuration、TypeHa

          本文链接:https://www.haomeiwen.com/subject/lgxgrctx.html