在spring-mybatis.xml配置文件中,
导入源码
public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
SqlSessionFactoryBean 实现了InitializingBean接口。要实现InitializingBean必须实现afterPropertiesSet方法。
afterPropertiesSet 初始化的时候执行,可以针对某个bean进行配置,就比如sqlSessionFactoryBean有afterPropertiesSet而其他bean不一定有。
执行顺序: 先afterPropertiesSet----initMethod---
如果有加后置处理器BeanPostProcessor,执行顺序 postProcessBeforeInitialization--afterPropertiesSet-initMethod--postProcessAfterInitialization
/**
* {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
继续跟踪,太长,先贴一段。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
//这块同一个this.configuration !=null条件但是用了两个if-else if-,按实现功能来分开。
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
//使用XMLConfigBuilder来解析。
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}//未完待续---段落A
跟踪XMLConfigBuilder,是调用了XPathParser。返回一个解析后的document对象。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
XPathParser是spring的核心包。是XPath的语法分析器。XPath主要作用就是用来解析xml文档。
XpathParser中大部分都是构造器,还有eval开头的方法。如evalBoolean、evalFloat等。主要作用调用evaluate. evaluate语法.
构造器则都调用了commonConstructor,该方法会实例化一个xpath.
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
综上所述,这段代码就是:用xpath语法来解析xml文档,解析成一个document对象。
/**
* Code B
* 对应的xml中的
* 扫描对应的包,注册成一个hashmap.
* hashmap的key-value : <首字母小写的类名,类的全路径>
*
*
*/
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
LOGGER.debug(() -> "Scanned package: '" + packageToScan + "' for aliases");
}
}
if (!isEmpty(this.typeAliases)) {
for (Class> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
}
}未完待续,接Code C
1.typeAliaserPackage是供扫描的Aliases的package。如下:
/**
* Packages to search for type aliases.
*
* @since 1.0.1
*
* @param typeAliasesPackage package to scan for domain objects
*
*/
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}
2.tokenizeToStringArray的作用是 用分隔符将TypeAliasesPackage分成数组。
3. 主要代码是
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
大致作用是将扫描到的类,注册成一个Aliases,Aliases是别名。通过配置别名,我们不用再指定完整的包名,并且还能取别名
跟踪进去
public void registerAliases(String packageName, Class> superType){
ResolverUtil> resolverUtil = new ResolverUtil>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set>> typeSet = resolverUtil.getClasses();
for(Class> type : typeSet){
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
AnonymousClass是匿名类,就是代码中没有明确指定的类,如直接new runnable接口,里面写实现方法。编译器会生成xxxx$1这样的class文件。
继续跟踪registerAlias方法。
public void registerAlias(String alias, Class> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
String key = alias.toLowerCase(Locale.ENGLISH);
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
TYPE_ALIASES.put(key, value);
}
其实是维护了一个 HashMap
private final Map> TYPE_ALIASES = new HashMap>();
/**
* Code C
* 对应的xml中的
* 扫描对应的包,注册成一个hashmap.
* hashmap的key-value : <首字母小写的类名,类的全路径>
*
*
*/
if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); LOGGER.debug(() -> "Registered plugin: '" + plugin + "'"); } }
/**
* Code D
* 对应的xml中的
* 扫描对应的包,注册成一个hashmap.
*
*
*
*/
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified or no matching resources found");
}
1. 先循环遍历mapperLocations。
2. 使用XMLMapperBuilder来解析包下的各个mapper xml文件。xmlMapperBuilder.parse()是解析mapper的方法。
跟踪parse方法。
resource是构造器中传入的configuration.getSqlFragments
跟踪下SqlFragements,发现是一个Map结构。StrictMap是单独写的一个实现类,增加了name属性,用来说明。
protected final Map sqlFragments = new StrictMap("XML fragments parsed from previous mappers");
如果configuration中没有加载过SqlFragement,加载过跳过。
跟踪进入configurationElement。
先拿到namespace属性,
再加载cache-ref,参照缓存是指原则上一个mapper分配一个cache对象,但是也可以设置cache-ref为多个Mapper来分配一个cache对象,
再加载cache二级缓存
再加载parameterMap入参
再加载resultMap 返回结果集
再加载sql。sql是将共同的select field 提出来,避免重复代码。
再加载select、insert、update、delete
1.分析加载cache-ref。
维护了一个chcheRefMap,将当前的namespace跟想ref去的namespace关联起来。
private void cacheRefElement(XNode context) {
if (context != null) {
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
跟踪津resolveCacheRef。具体实现如下。用boolean变量来防止出错。
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true;
Cache cache = configuration.getCache(namespace);
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
currentCache = cache; //起作用的code
unresolvedCacheRef = false;
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
2
.分析加载cache。
private void cacheElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");//回收机制,如未设值,默认LRU最近最少使用
Class extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
cache有一个实现类PerpetualCache.本质是一个hashMap。
3.分析加载parameterMap。
private void parameterMapElement(List list) throws Exception {
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
Class> parameterClass = resolveClass(type);
List parameterNodes = parameterMapNode.evalNodes("parameter");
List parameterMappings = new ArrayList();
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
ParameterMode modeEnum = resolveParameterMode(mode);
Class> javaTypeClass = resolveClass(javaType);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
@SuppressWarnings("unchecked")
Class extends TypeHandler>> typeHandlerClass = (Class extends TypeHandler>>) resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}