【MyBatis】执⾏原理(⼀):创建会话⼯⼚(SqlSessionFactory)--配。。。要分析 mybatis 的执⾏原理,⾸先要搞清楚的就是它如何解析配置⽂件的。
PS:配置⽂件其实有两种,⼀个是由许多标签构成的l全局配置⽂件,另⼀个是可能有多个的l⽂件。
本篇我们只看全局配置⽂件是如何解析的。
在 我们说了如何使⽤ mybatis
String resource ="l";
InputStream inputStream = ResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
// 在创建SqlSessionFactory就可以去获取SqlSession执⾏sql了
所以,解析配置⽂件的奥秘就在 SqlSessionFactoryBuilder#build 中,我们下⾯就从源码中寻找答案…SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 采⽤了建造者模式,是⽤来构建 SqlSessionFactory 的
SqlSessionFactory 只需要⼀个(单例的),所以只要构建了这⼀个 SqlSessionFactory,它的使命就完成了,也就没有存在的意义了。所以它的⽣命周期只存在于⽅法的局部。
noon是什么意思我们打开 SqlSessionFactoryBuilder,可以发现⾥⾯全是 build ⽅法
// ⼊⼝的build⽅法
public SqlSessionFactory build(Reader reader){
return this.build((Reader)reader,(String)null,(Properties)null);
}
// 1.获取XMLConfigBuilder对象
// 2.通过XMLConfigBuilder建造Configuration对象(单例)
// 3.通过Configuration构造SqlSessionFactory(单例)
public SqlSessionFactory build(Reader reader, String environment, Properties properties){
XMLConfigBuilder parr =new XMLConfigBuilder(reader, environment, properties);
// XMLConfigBuilder#par()会返回⼀个Configuration对象
6级真题
return build(parr.par());
//...
}
// 最终的build⽅法
public SqlSessionFactory build(Configuration config){
// SqlSessionFactory 是⼀个接⼝
// 所以,根据配置信息返回⼀个默认的实现类实例 SqlSessionFactory
// 注意,这⾥再构造 DefaultSqlSessionFactory 时传⼊了 Configuration(后⾯会⽤到)
return new DefaultSqlSessionFactory(config);
}
所以,我们现在要解决的就是下⾯两个问题:
1. Configuration 到底是什么?
2. XMLConfigBuilder#par() 是如何构造出来⼀个 Configuration 的?
1.Configuration
Configuration 包含了 MyBatis 所有的配置信息,源码如下:
关于 l 中可以配置什么,可以参考 …
public class Configuration {
// 数据源
protected Environment environment;
/
/ 很多设置都是tting标签的,有默认值
protected boolean safeRowBoundsEnabled;check out
protected boolean safeResultHandlerEnabled =true;
protected boolean mapUnderscoreToCamelCa;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled =true;
protected boolean uGeneratedKeys;
protected boolean uColumnLabel =true;
// 缓存
protected boolean cacheEnabled =true;
protected boolean callSettersOnNulls;
protected boolean uActualParamName =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 Integer defaultStatementTimeout;
protected Integer defaultFetchSize;
databusprotected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected Properties variables =new Properties();
protected ReflectorFactory reflectorFactory =new DefaultReflectorFactory();
// ObjectFactory
protected ObjectFactory objectFactory =new DefaultObjectFactory();
// ObjectWrapperFactory
protected ObjectWrapperFactory objectWrapperFactory =new DefaultObjectWrapperFactory();
// 懒加载
protected boolean lazyLoadingEnabled =fal;
// 代理⼯⼚
protected ProxyFactory proxyFactory =new JavassistProxyFactory();
protected String databaId;
protected Class<?> configurationFactory;
// Registrys
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, 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 co
llection"); protected final Map<String, KeyGenerator> keyGenerators =new StrictMap<>("Key Generators collection");
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<>();
protected final Map<String, String> cacheRefMap =new HashMap<>();
//...methods
}
⽅⽆⾮就两⼤类 ,读(get)和写(t/add)。t 主要是对于上⾯懒加载那些只有⼀个属性的,⽽ add 第对于那些以map为数据结构的registry ⽽⾔的,⽐如添加缓存
// 添加缓存到map中
public void addCache(Cache cache){
this.caches.Id(), cache);
}
// 获取全部缓存
public Collection<Cache>getCaches(){
return this.caches.values();
}
// 获取指定缓存
public Cache getCache(String id){8810
return(Cache)(id);
}
下⾯我们继续去看第⼆个问题,XMLConfigBuilder#par 是如何构造 Configuration 对象的。
2.XMLConfigBuilder
我们先来看看 XMLConfigBuilder 的⽗类 BaBuilder,
// XMLConfigBuilder的抽象⽗类,专门⽤来解析全局配置⽂件
// 另外还有⼦类XMLMapperBuilder(解析Mapper映射器),XMLStatementBuilder(解析增删改查标签)
public abstract class BaBuilder {
// Configuration 是其中⼀个成员变量
protected final Configuration configuration;
// 别名库,是构造出的 Configuration 的 typeAliasRegistry 的引⽤
// 作⽤是 typeAlias 标签中的别名关系直接加到这⾥,不⽤再调⽤ configuration.add
protected final TypeAliasRegistry typeAliasRegistry;
// 类型处理器库,typeHandler标签中的类型处理器直接加到这⾥,不⽤再调⽤configuration.add
protected final TypeHandlerRegistry typeHandlerRegistry;
// 构造函数传⼊Configuration
public BaBuilder(Configuration configuration){
执业药师试听// 别名库与类型处理器库实际已经在Configuration对象中的
}
//.......
}
XMLConfigBuilder()
// XMLConfigBuilder构造函数
private XMLConfigBuilder(XPathParr parr, String environment, Properties props){
// 这⾥先直接创建⼀个Configuration,然后传⼊ BaBuilder
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.pard =fal;
this.parr = parr;
}
par()*
解析配置⽂件构造 Configuration 的⼊⼝⽅法
public Configuration par(){
// 配置⽂件只解析⼀次
if(pard){
throw new BuilderException("Each XMLConfigBuilder can only be ud once.");
}
pard =true;
// 对Configuration根据传⼊的具体配置信息进⾏解析
parConfiguration(parr.evalNode("/configuration"));
/
/ 返回Configuration
return configuration;
}
// 核⼼逻辑都在下⾯的⽅法中
outpost
private void parConfiguration(XNode root){
try{
// 解析Properties标签,读取引⼊的外部配置⽂件
propertiesElement(root.evalNode("properties"));
// 将 tting 标签解析成 Properties 对象,对于 ttings ⼦标签的处理在后⾯
Properties ttings =ttingsAsProperties(root.evalNode("ttings"));
// 获取Vitual File System的⾃定义实现类,⽐如我们要读取本地⽂件,或者FTP远程⽂件时,就要⽤到⾃定义VFS类。loadCustomVfs(ttings);
// 根据 logImpl 标签获取⽇志的实现类,我们可以⽤到很多的⽇志的⽅案,包括 LOG4J,LOG4J2,SLF4J 等等。loadCustomLogImpl(ttings);
// 解析 typeAlias 标签(别名),注册到 TypeAliasRegistry
typeAliasElement(root.evalNode("typeAlias"));
// 解析plugins标签,⽐如Pagehelper翻页插件,或者⾃定义插件
pluginElement(root.evalNode("plugins"));
// 解析objectFactory标签,⽣成ObjectFactory对象,设置到Configuration⾥⾯
objectFactoryElement(root.evalNode("objectFactory"));
// 解析objectWrapperFactory标签,⽣成ObjectWrapperFactory对象,设置到Configuration⾥⾯objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 处理 ttings 的⼦标签,⽅法⼊参是之前解析的 properties对象
ttingsElement(ttings);
// 解析environments标签
environmentsElement(root.evalNode("environments"));
databaIdProviderElement(root.evalNode("databaIdProvider"));
// 解析typeHandler标签,将类型处理器注册到 typeHandlerRegistry
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper映射关系,保存到mapperRegistry
mapperElement(root.evalNode("mappers"));
}catch(Exception e){
throw new BuilderException("Error parsing SQL Mapper Configuration. Cau: "+ e, e);
}
}
propertiesElement()
解析Properties标签,读取引⼊的外部配置⽂件
private void propertiesElement(XNode context)throws Exception {
if(context != null){
Properties defaults = ChildrenAsProperties();
// 1.获取配置⽂件路径
// 1.1 放在resource⽬录下,相对路径
String resource = StringAttribute("resource");
// 1.2 绝对路径
String url = StringAttribute("url");
if(resource != null && url != null){
throw new BuilderException("The properties element cannot specify both a URL and a resource bad property file reference. Plea specify one or th e other.");
}
// 2.将配置信息放到名为defaults的Properties对象⾥⾯
if(resource != null){
defaults.ResourceAsProperties(resource));
}el if(url != null){
defaults.UrlAsProperties(url));
}
マンコProperties vars = Variables();
if(vars != null){
我是歌手总决赛黄绮珊defaults.putAll(vars);
}
// 3.把XPathParr 和 Configuration 的 Properties 属性都设置成我们填充后的 Properties对象
parr.tVariables(defaults);
configuration.tVariables(defaults);
}
}
ttingsAsProperties()
将 tting 标签解析成 Properties 对象,对于 ttings ⼦标签的处理在后⾯。
在早期的版本⾥⾯解析和设置都是在后⾯⼀起的,这⾥先解析成Properties对象是因为下⾯的两个⽅法要⽤到。
private Properties ttingsAsProperties(XNode context){
if(context == null){
return new Properties();
}
Properties props = ChildrenAsProperties();
// Check that all ttings are known to the configuration class
// 检查配置类是否知道所有设置
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for(Object key : props.keySet()){
if(!metaConfig.hasSetter(String.valueOf(key))){
throw new BuilderException("The tting "+ key +" is not known. Make sure you spelled it correctly (ca nsitive).");
}
}
return props;
}
ttingsElement()*
处理 ttings 的⼦标签,⽅法⼊参是之前解析的 properties对象。
PS:如果没有⼿动设置的属性,这⾥会设置默认值,然后放⼊Configuration对象