简述下Configuration,他的作用是什么?
- Configuration
- 这个类用于描述MyBatis主配置文件信息,MyBatis框架在启动时会解析加载主配置文件,将配置信息存储进Configuration对象中。包含配置如数据源,事务管理器,类型别名,类型处理器,对象工厂,插件,环境,数据库标识,映射器等。
- 在mybatis启动之后的后续流程如sql查询,需要获取mybatis配置信息时直接从Configuration中获取即可,达成一次解析,多次使用的目的。
- Configuration对象是MyBatis的核心,它贯穿了MyBatis的整个生命周期,几乎随处可见,地位类比于Spring框架的ApplicationContext
Configuration类结构
先将Configuration分为两部分看,一部分为成员变量,一部分为方法。
成员变量一览
可以看到,该类的成员变量数量多的离谱。
以下代码截取了Configuration类的成员变量部分,同时对一些常见类型进行了注释说明。
//数据库信息,连接地址,账号密码,事务配置等
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 boolean shrinkWhitespacesInSql;
protected String logPrefix;
//日志具体实现类
protected Class<? extends Log> logImpl;
protected Class<? extends VFS> vfsImpl;
protected Class<?> defaultSqlProviderType;
//一级缓存生命周期
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 ResultSetType defaultResultSetType;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
//配置文件中的properties标签对应的k,v
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://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
//拦截器链,存储自定义插件list
protected final InterceptorChain interceptorChain = new InterceptorChain();
//类型转换器注册器
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
//类型别名器注册器
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//sql对象,一个sql存储在一个MappedStatement中
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");
//存储所有resultMap
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
//存储所有rparameterMap
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
//存储所有keyGenerator
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<>();
部分方法一览
可以看到方法大部分都是对成员变量做get,set的,以及一些校验。所以判断出,Confiugration的作用正如前文所示,是用来存储各种变量的,基本没有其他逻辑操作。
Configutation是怎么装配各项属性的?
装配属性核心方法寻找
我们通过xml配置文件获取参数为例,看看Configuration是在哪个阶段进行属性装配的。
main方法如下
debug进SqlSessionFactoryBuilder.build方法,发现Configuartion由方法XMLConfigBuilder.parse创建
进入XMLConfigBuilder.parse方法,看到这里执行了parseConfiguration方法,接着就返回类变量configuration了,可以看出,Configuration类的解析在该方法中
parseConfiguration中执行了很多方法,主要内容为xml配置文件中,各项标签配置的读取。如properties,settings,plugins,environments等。
Configutation的属性解析过程
从Configuration的类结构可知,核心在于成员变量,所以我们主要看看成员变量的赋值过程。以下挑几个变量为例进行说明。
environment
就从第一个成员变量说起。排除绿色的单元测试类引用处,查看引用可以看到environment都是通过getEnvironment,setEnvironment方法被使用的
继续看setEnvironment方法,被XmlConfigBuilder的environmentsElement调用及SqlSessionFactoryBean调用。
通过类名称也可以大概猜到,XmlConfigBuilder是对xml文件进行属性解析,SqlSessionFactoryBean名字带个Bean字,是通过读取spring环境变量配置进行属性解析。
由于示例为xml文件形式读取的配置文件,我们继续看XmlConfigBuilder的environmentsElement方法。看到是一堆xml文件标签的解析。所以判定XNode对象就是environments标签对应的对象。
实际也的确如此。获取xml文件根标签configuration下的environments标签进行解析。
比较一下解析的方法跟xml文件的属性,可以看到的确是一一对应的,读取了xml中事务管理器跟数据源的配置。
读取完之后通过environmentBuilder.build()生成了environment对象,set进Configuration中。
看看environmentBuilder.build(),执行了什么,其实只是把环境id,事务管理器跟数据源放进去而已,没有其他操作。这就是Configuration中的成员变量之一:environment的赋值过程。
logImpl
这个属性用于配置mybatis使用的日志框架。我们用同样的流程看看该属性是怎么被赋值的
排除绿色的单元测试类引用,也能看到是setLogImpl方法进行赋值的。
发现setLogImpl方法,被XMLConfigBuilder的loadCustomLogImpl方法所调用,可以看到mybatis日志具体类对象在该方法中被创建及赋值,具体是通过Properties中logImpl属性而来的。
又回到最初的起点XMLConfigBuilder的loadCustomLogImpl也是parseConfiguration调用的。Properties也是此方法生成的。
看看Properties是啥,原来是一个HashTable的子类,也就是存储<K,V>形式的值
debug看一下,的确如此,将xml配置文件中,/configuration标签下的,settings标签下的值解析成<K,V>形式。
跟xml文件的值是一一对应的,这就是mybatis中日志实现类:logImpl的赋值流程。
以上举例了Configuration两个成员变量的赋值流程,剩下的大部分变量的赋值流程也大差不差,通过源码发现也有两个变量:typeAliasRegistry,languageRegistry 是在构造方法中赋值的,意思是写死一些类型对应的类跟一些语言对应的类。