Mybatis之SqlSessionFactoryBean源码初步解析(⼀)
⾸先先道歉⼀下, 我之前并没有研究太深⼊,然后发现有些东西写错了。然后7⽉7 号,忙到夜⾥2点改这篇⽂章。改完之后太累了就关了电脑。。。然后没保存发布,⼜重写了⼀遍。
Mybatis 的两个⼊⼝⼀个是SqlSessionFactoryBean, 另外⼀个是MapperScannerConfigurer。这个可以在l 配置中到。SqlSessionFactoryBean实现了InitializingBean接⼝,InitializingBean接⼝必须得实现afterpropertiesset()⽅法,此⽅法将配置⽂件中的各种属性列如 mapperlocation 、typealias、plugins等等放⼊configuration中,configuration就相当于mybatis的⼤管家, 各种解析封装后的属性都会放到这⾥。 然后您会看到
else if (figLocation != null) {
xmlConfigBuilder = new InputStream(), null, figurationProperties);
configuration = Configuration();
xmlConfigBuilder这个东西是解析xml 的⼀个类
xmlConfigBuilder = new InputStream(), null, figurationProperties);
这⾥就是装配配置⽂件
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + figLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + figLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
继续往下运⾏的时候会发现
xmlConfigBuilder.parse();
这个就是开始解析xml了 这边是专门解析下图中configLocation 对应的xml
<configuration>
<!-- 全局的setting配置根据需要添加需要⼆级缓存延迟加载就可以配置-->
<!-- 配置别名 -->
<typeAliases>
<!-- 批量扫描别名 -->
<package name="batis.pojo"/>
<!--<package name="cn.zx.ssm.po"/>  -->
</typeAliases>
<!-- 使⽤⾃动扫描器时,l⽂件如果和mapper.java接⼝在⼀个⽬录则此处不⽤定义mappers    -->
<mappers>
<package name="batis.mapper" />
</mappers>
</configuration>
这个xml ⾥⾯的最外层标签是configuration;所以下⾯最先解析的是configuration
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
有configuration标签的话就开始解析了。然后我们主讲⼀下mappers 标签。
mapperElement(root.evalNode("mappers"));
这句话就是解析mappers的⼦元素了
如果⼦元素是package 那么调⽤configuration的addMappers()将包路径扔给configuration.
如果mappers标签的⼦元素标签不是package 。那么得到他⼦元素3个属性的值,分别是resource、url、class 。三者只能同时存在⼀个。同时存在多以⼀个则报异常。因为configuration.addMappers()⽅法最终也是调到XMLMapperBuilder.
configuration.addMappers() ⾥⾯是调⽤mapperRegistry.addMappers()。
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : Children()) {
if ("package".Name())) {
String mapperPackage = StringAttribute("name");
error parse new
configuration.addMappers(mapperPackage);
} else {
String resource = StringAttribute("resource");
String url = StringAttribute("url");
String mapperClass = StringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = ResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, SqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = UrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, SqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
我对于mapperRegistry是哪⾥来的 很感兴趣。于是我看到了下⾯代码所⽰
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
这⾥的this 代表的就是Configuration这个类。
进⼊mapperRegistry.addMappers() ⽅法会进⼊下图。 先调⽤下⾯代码中最后⼀个⽅法, 再调⽤上⼀个⽅法。
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = Classes();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
这边再道个歉。 先不上图了,本来有图的 我忘了保存。 我就嘴说说吧
这边的mapperSet 呢解析出来是什么格式呢:
[interface batis.mapper.DailyMapper, interface batis.mapper.EmployeeMapper]
意思就是interface+全路径。 很有意思的是:⽐如我这样配置的
<package name="batis.mapper" />
那么只会解析batis.mapper下的。 如果你的xml放在st。这个包下, 那么是不会在mapperSet中的。注意看图下的size.
我们继续看调到addMapper()⽅法:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
}
}
}
}
hasMapper(type)⽅法调⽤的是什么呢?看下⾯代码
public <T> boolean hasMapper(Class<T> type) {
ainsKey(type);
}
意思就是看看knownMappers ⾥⾯有没有,即是缓存⾥⾯存在不存在。knownMappers 这个属性很重要 再之后的代码中会出现正到。如果没有的话就存到knownMappers 中
这个 newMapperProxyFactory()重中之重 ⼲了什么 呢? 没错动态代理, 掉个胃⼝,下次再说。
然后要看MapperAnnotationBuild这个⽅法了。
type传进去只为了再后⾯拼接⼀个.java 然后赋值给resource属性
我们主要看parse.parse();
下⾯代码 中主要的是loadXmlResource(), 和parseStatement(method);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。