Configuration类是Mybatis中的特别核心的一个类,主要用来进行Mybatis执行过程中的各项参数的设置。第一次Debug源码时,会感觉到什么配置都需要在Configuration中设置,多次Debug之后,发现确实如此,这就是Mybatis中的核心配置类。。。2333
因为在Mybatis的整个生命周期中,只存在一个Configuration的实例。这里没有使用单例模式,所以在Configuration实例化之前,完成了对成员变量的实例化,所以可以访问其中的方法。并且这些成员变量是final
的永远指向同一个对象实例。
Mybatis的所有配置参数都在Configuration
中。
下面开始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 extends Log> logImpl;
protected Class extends VFS> 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();
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);
}
这里主要分析MapperRegistry
、InterceptorChain
、TypeHandlerRegistry
、TypeAliasRegistry
、LanguageDriverRegistry
的实例化。
MapperRegistry实例化:主要是将Configuration传递给MapperRegistry,而MapperRegistry中维护了一个HashMap, 提供对Mapper的添加、判断是否存在、获取操作。
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
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
。在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);
}
}
拦截器链,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);
}
}
主要用来注册类型转换器。
TypeHandler也就是我们在Mapper.xml中常见的jdbcType与javaType之间的映射关系。
内部使用HashMap来记录类型对应关系。
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
这里使用了泛型,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());
}
主要为java类设置别名,我们在xml中配置的TypeAlias也会被注册进来(详细执行过程见XMLConfigBuilder中的typeAliasesElement()方法)。
内部还是HashMap实现。Map
在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);
}
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());
}
1.首先Configuration是在XMLConfigBuilder的private XMLConfigBuilder(XPathParser parser, String environment, Properties props)
构造器中创建的,这里涉及到XML配置文件解析成Configuration参见 mybatis源码学习之执行过程分析(2)——Mapper接口和XML文件映射及解析中的分析。
2.Configuration中的MapperRegistry
、InterceptorChain
、TypeHandlerRegistry
、TypeAliasRegistry
、LanguageDriverRegistry
在Configuration构造方法调用之前进行了实例化和基本的设置。最后由Configuration中的final修饰的成员变量引用,所以在Mybatis的生命周期中,可以通过Configuration实例获取到这些注册的信息。