mybatis源码学习——Configuration类及其初始化过程、TypeHandler、TypeAlias

Configuration类是Mybatis中的特别核心的一个类,主要用来进行Mybatis执行过程中的各项参数的设置。第一次Debug源码时,会感觉到什么配置都需要在Configuration中设置,多次Debug之后,发现确实如此,这就是Mybatis中的核心配置类。。。2333

因为在Mybatis的整个生命周期中,只存在一个Configuration的实例。这里没有使用单例模式,所以在Configuration实例化之前,完成了对成员变量的实例化,所以可以访问其中的方法。并且这些成员变量是final的永远指向同一个对象实例。

Mybatis的所有配置参数都在Configuration中。

下面开始Configuration源码的分析~~

1.Configuration中的成员变量

可以看到Configuration中的所有成员变量的访问权限都是protected ,只允许org.apache.ibatis.session中的类访问。

  //主要用来配置事务工厂和数据源,以及environment的id。
  //可以使用多个环境,例如开发环境、测试环境,以id来区分。
  protected Environment environment;

  protected boolean safeRowBoundsEnabled = false;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase = false;  //数据库表字段驼峰命令 
  protected boolean aggressiveLazyLoading = true;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys = false;        //自动生成主键
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;            //默认缓存是开启的
  protected boolean callSettersOnNulls = false;
  protected boolean useActualParamName = true;

  protected String logPrefix;
  protected Class  logImpl;
  protected Class  vfsImpl;

  //缓存的作用域。
  //SESSION表示会缓存一个会话中执行的所有查询。 
  //STATEMENT表示本地会话仅用在语句执行上,对相同SqlSession的不同调用将不会共享数据。 
  //具体缓存,后面分析
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;   
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set lazyLoadTriggerMethods = new HashSet(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;

  /**
  * 默认ExecutorType为SIMPLE,但实际使用过程中因为cacheEnabled = true而使用了CachingExecutor。
  * ExecutorType有[SIMPLE, REUSE, BATCH],
  *               REUSE使用PrepareStatement,
  *               BATCH表示可以批量执行(这种模式下可以使用sqlSession中的flushStatements())
  */
  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;

  protected Class configurationFactory;

  //创建MapperRegistry并将Configuration传递给MapperRegistry
  //MapperRegistry主要用来管理Mapper接口
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

  //拦截器链,赋值在Executor创建的时候

  //重点:类型转换的注册,在这里注册了常用的类型转换器。当不能满足开发需要时,可以自定义,例如0,1与false,true的类型转换器。
  //也就是Java中的类型与数据库中支持的类型的相互转换关系的定义。
  //TypeHandlerRegistry主要用来管理TypeHandler
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

  //重点:类别名的注册。方便我们在使用mapper.xml中的使用。例如在paramterType中我们可以直接写"int",会被映射为java.lang.Integer
  //TypeAliasRegistry所有的对应关系被记录在变量TYPE_ALIASES引用的HashMap中。
  //并包含一个  public  Class resolveAlias(String string) 的方法,用来根据String类型的key获取对应的类。

  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();


//这几个都使用了StrictMap是HashMap的一个子类
//先打 TODO 后面再分析吧。。。
  protected final Map mappedStatements = new StrictMap("Mapped Statements collection");
  protected final Map caches = new StrictMap("Caches collection");
  protected final Map resultMaps = new StrictMap("Result Maps collection");
  protected final Map parameterMaps = new StrictMap("Parameter Maps collection");
  protected final Map keyGenerators = new StrictMap("Key Generators collection");



  protected final Set loadedResources = new HashSet();
  protected final Map sqlFragments = new StrictMap("XML fragments parsed from previous mappers");

  protected final Collection incompleteStatements = new LinkedList();
  protected final Collection incompleteCacheRefs = new LinkedList();
  protected final Collection incompleteResultMaps = new LinkedList();
  protected final Collection 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 cacheRefMap = new HashMap();

2.Configuration的构造函数

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  //然后是Configuration默认构造函数的
  public Configuration() {
  //为常用配置名注册别名
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);   //JDBC注册了JdbcTransactionFactory工厂
    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);   //默认为XML
    languageRegistry.register(RawLanguageDriver.class);
  }

3.Configuration中成员变量的初始化

这里主要分析MapperRegistryInterceptorChainTypeHandlerRegistryTypeAliasRegistryLanguageDriverRegistry的实例化。

3.1.MapperRegistry的实例化

MapperRegistry实例化:主要是将Configuration传递给MapperRegistry,而MapperRegistry中维护了一个HashMap, 提供对Mapper的添加、判断是否存在、获取操作。

 protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

mybatis源码学习——Configuration类及其初始化过程、TypeHandler、TypeAlias_第1张图片

public class MapperRegistry {

  private final Configuration config;  //这里的变量还是final修饰的
  private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  ...
}

当使用Configuration中的getMapper方法时,其实调用了MapperRegistry中的public T getMapper(Class type, SqlSession sqlSession) 。在knownMappers的HashMap中拿到MapperProxyFactory代理工厂。

Configuration.java

  public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
MapperRegistry.java

  public  T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

3.2.InterceptorChain

拦截器链,ArrayList实现。

public class InterceptorChain {

  private final List interceptors = new ArrayList();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

3.3.TypeHandlerRegistry

主要用来注册类型转换器。
TypeHandler也就是我们在Mapper.xml中常见的jdbcType与javaType之间的映射关系。
内部使用HashMap来记录类型对应关系。

mybatis源码学习——Configuration类及其初始化过程、TypeHandler、TypeAlias_第2张图片

这里写图片描述

1.TypeHandler接口定义了获取sql查询结果的3种方式,以及设置参数的方法。可以看到,传递参数使用了JDBC的PreparedStatement,预处理sql。3中重载getResult()提供了根据列名,列索引获取sql查询结果的方法。实际上是对ResultSet和CallableStatement的封装。

2.BaseTypeHandler是个抽象类,并实现了TypeHandler接口。

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  protected Configuration configuration;

  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }

3.在这里所有的TypeHandler都继承自BaseTypeHandler。
同时使用了模板方法设计模式,定义了执行过程,具体的获取结果的方式,交给子类去实现。这样设计有极大的灵活性,当不能满足开发需要时,我们可以定义自己的TypeHandler~~

自定义TypeHandler的加载见XMLConfigBuilder#parseConfiguration(XNode root)#typeHandlerElement(root.evalNode("typeHandlers"));

同时可以看到BaseTypeHandler中持有Configuration的引用,所以说Configuration真是贯穿于Mybatis的整个生命周期。。。2333

4.那我们怎知道什么时候调用String类型的TypeHandler什么时候调用Integer类型的TypeHandler呢? 再看BaseTypeHandler的定义 class BaseTypeHandler extends TypeReference implements TypeHandler这里使用了泛型,T继承自TypeReference。这里是关键,TypeReference里记录了rawType,所以可以准确的调用到对应的TypeHandler去getResult。

5.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(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 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(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, 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());

    // mybatis-typehandlers-jsr310
    try {
      // since 1.0.0
      register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");
      register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");
      register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");
      register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");
      register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");
      register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");
      register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");
      // since 1.0.1
      register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");
      register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");

    } catch (ClassNotFoundException e) {
      // no JSR-310 handlers
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

3.4.TypeAliasRegistry的实例化

主要为java类设置别名,我们在xml中配置的TypeAlias也会被注册进来(详细执行过程见XMLConfigBuilder中的typeAliasesElement()方法)。

内部还是HashMap实现。Map> TYPE_ALIASES

在Configuration的默认构造方法中,注册了”JDBC”等全局的别名。

public class TypeAliasRegistry {

  private final Map> TYPE_ALIASES = new HashMap>();

  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);
  }

4.再说Configuration

Configuration的这种设计虽然违背了单一职责的设计原则,但也带来了极大的好处,我们可以在sqlsession、sqlsessionFactory中拿到Configuration进而拿到所有设置。

例如:

//获取所有注册的别名
Configuration configuration = sqlSessionFactory.getConfiguration();
TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
Map> typeAliases = typeAliasRegistry.getTypeAliases();
for (Map.Entry> entry : typeAliases.entrySet()) {
    System.out.println(entry.getKey() + "  " + entry.getValue());
}

//获取所有已注册的类型转换器
TypeHandlerRegistry typeHandlerRegistry = session.getConfiguration().getTypeHandlerRegistry();
Collection> typeHandlers = typeHandlerRegistry.getTypeHandlers();
for (Map.Entry> entry : typeAliases.entrySet()) {
    System.out.println(entry.getKey() + "  " + entry.getValue());
}

5.总结

1.首先Configuration是在XMLConfigBuilder的private XMLConfigBuilder(XPathParser parser, String environment, Properties props)构造器中创建的,这里涉及到XML配置文件解析成Configuration参见 mybatis源码学习之执行过程分析(2)——Mapper接口和XML文件映射及解析中的分析。

2.Configuration中的MapperRegistryInterceptorChainTypeHandlerRegistryTypeAliasRegistryLanguageDriverRegistry在Configuration构造方法调用之前进行了实例化和基本的设置。最后由Configuration中的final修饰的成员变量引用,所以在Mybatis的生命周期中,可以通过Configuration实例获取到这些注册的信息。

你可能感兴趣的:(框架学习,mybatis源码)