前言
上一篇文章中简单的介绍了Mybatis的执行流程.
执行的第一步就是构建一个SqlSessionFactory, 那到底我们是如何获取到SqlSessionFactory的呢?
接下来让我们一起学习一下吧!
1.SqlSessionFactory源码详解
SqlSessionFactory是一个接口,引用代码的文档解释: 它的作用就是从Connection或者DataSource中创建一个SqlSession
它有两个实现DefaultSqlSessionFactory和SqlSessionManager
public interface SqlSessionFactory {
/**获取一个默认配置的SqlSession
* @return 默认配置的SqlSession
*/
SqlSession openSession();
/**获取一个指定事务提交方式的SqlSession
* @param autoCommit 事务提交方式, true,false 自动或手动
* @return 指定事务提交方式的SqlSession
*/
SqlSession openSession(boolean autoCommit);
/**获取一个给定数据库连接的SqlSession
* @param connection 数据库连接
* @return 指定数据库连接的SqlSession
*/
SqlSession openSession(Connection connection);
/**获取一个给定事务隔离级别的SqlSession
* @param level 事务隔离级别
* @return 给定事务隔离级别的SqlSession
*/
SqlSession openSession(TransactionIsolationLevel level);
/**获取一个给定执行器类型的SqlSession
* @param execType DefaultSqlSession中维护的一个执行器的类型
* @return 给定执行器类型的SqlSession
*/
SqlSession openSession(ExecutorType execType);
/**获取一个给定执行器类型和事务提交方式的SqlSession
* @param execType DefaultSqlSession中维护的一个执行器的类型
* @param autoCommit 事务提交方式, true,false 自动或手动
* @return 给定执行器类型和事务提交方式的SqlSession
*/
SqlSession openSession(ExecutorType execType, boolean autoCommit);
/**获取一个给定执行器类型和事务隔离级别的SqlSession
* @param execType DefaultSqlSession中维护的一个执行器的类型
* @param level 事务隔离级别
* @return 给定执行器类型和事务隔离级别的SqlSession
*/
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
/**获取一个给定执行器类型和数据库连接的SqlSession
* @param execType DefaultSqlSession中维护的一个执行器的类型
* @param connection 数据库连接
* @return 给定执行器类型和数据库连接的SqlSession
*/
SqlSession openSession(ExecutorType execType, Connection connection);
/**获取核心配置文件对象
* @return 核心配置文件对象
*/
Configuration getConfiguration();
}
1.1 DefaultSqlSessionFactory源码详解
DefaultSqlSessionFactory是SqlSessionFactory的默认实现,接口的openSession(xxx …)的实现都是通过内部的两个函数实现
openSessionFromDataSource() 和 openSessionFromConnection()
这也和接口的文档上描述的一致
openSessionFromDataSource()和openSessionFromConnection() 的实现对比
他们的主体实现基本一致,区别是在于事务的构造上
openSessionFromDataSource 是通过数据源和参数指定的事务隔离级别,参数指定的是否自动提交来构建事务
openSessionFromConnection 是通过连接中的属性来构建事务
它们最终返回的都是DefaultSqlSession
/**从数据源中获取SqlSession
* @param execType 执行器的类型
* @param level 事务隔离级别
* @param autoCommit 事务提交方式, true,false 自动或手动
* @return DefaultSqlSession
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//读取环境参数
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//使用事务工厂创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建一个sql执行器
final Executor executor = configuration.newExecutor(tx, execType);
//构建一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
//如果失败,关闭事务
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**从连接中获取SqlSession
* @param execType 执行器的类型
* @param connection 数据库连接
* @return DefaultSqlSession
*/
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
//从连接中获取事务提交类型
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
//默认为true, 有的数据库或驱动不支持事务
autoCommit = true;
}
//读取环境参数
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//使用事务工厂创建事务
final Transaction tx = transactionFactory.newTransaction(connection);
//创建一个sql执行器
final Executor executor = configuration.newExecutor(tx, execType);
//构建一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
1.2 SqlSessionManager源码详解
SqlSessionManager实现了SqlSessionFactory和SqlSession两个接口,本章我们主要学习下SqlSessionFactory相关的接口.
后续再进行SqlSession的学习
SqlSessionManager内部维护了一个SqlSessionFactory和一个SqlSession对象还有一个ThreadLocal变量,他提供的newInstance()方法都是创建一个DefaultSqlSessionFactory,然后调用私有的构造方法进行构造.所以他的openSession()相关方法的实现都是通过DefaultSqlSessionFactory去实现的
从上述的学习我们可以发现DefaultSqlSessionFactory目前都是通过SqlSessionFactoryBuilder的build()方法构建的,接下来我们就来学习SqlSessionFactoryBuilder
2. SqlSessionFactoryBuilder源码详解
打开SqlSessionFactory源码, 我们可以看见提供了build()方法的9种重载方式
具体可以分为以下三类
1. 以Reader为基础
前四个方法都是以Reader为基础的, 真正逻辑的实现在第四个方法,前三个方法通过传递默认参数null进行内部调用
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
2. 以InputStream为基础
第五到第八个方法都是以InputStream为基础的, 真正逻辑的实现在第八个方法,前三个方法通过传递默认参数null进行内部调用
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
3. 以Configuration为基础
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
其实第一种和第二种的实现方式都是类似,通过构建XMLConfigBuilder,然后调用parse()方法获得一个Configuration对象.
最终调用第三种方式获取SqlSessionFactory
自己YY
第二种方式也可以改造成如下方式, 通过InputStreamReader来包装InputStream的方式获得一个Reader,然后调用以Reader为基础的方法进行操作
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
return build(new InputStreamReader(inputStream), environment, properties);
}
3. XMLConfigBuilder源码详解
我们对XMLConfigBuilder进行简单的分解
- 六个公共的构造方法提供调用
- 一个私有的构造方法,是仅供内部调用, 六个公共的构造方法最终都是通过私有方法去实现初始化
- 一个公共的parse()方法,调用了内部的N个私有方法
- 一堆私有的方法, 在parse()中被调用
1.私有的构造方法解析
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
在这个构造方法中主要做了两件事
- 调用父类的构造方法,并创建一个Configuration对象传入
- 将参数设置到属性中
2.parse()方法解析
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在parse()方法中完成的逻辑
- 判断是否解析过,没解析则进行解析,并返回Configuration对象
- 如果已经被解析过,则抛出异常,一个XMLConfigBuilder只能解析一次
3.parseConfiguration(XNode root)方法解析
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
//解析properties然后放入Configuration中
propertiesElement(root.evalNode("properties"));
//解析settings
Properties settings = settingsAsProperties(root.evalNode("settings"));
//VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源
loadCustomVfs(settings);
//自定义log实现
loadCustomLogImpl(settings);
//处理别名
typeAliasesElement(root.evalNode("typeAliases"));
//处理插件
pluginElement(root.evalNode("plugins"));
//处理自定义对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//处理自定义对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//处理反射工厂
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//解析自定义setting,并设置到Configuration
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析环境配置
environmentsElement(root.evalNode("environments"));
//解析数据库提供厂商
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//解析自定义mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
4. 解析Properties
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//读取所有子节点构建一个Properties, 遍历读取name和value属性,然后进行赋值
Properties defaults = context.getChildrenAsProperties();
//读取resource
String resource = context.getStringAttribute("resource");
//读取url
String url = context.getStringAttribute("url");
//url和resource不能共存
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
//从resource中解析Properties,并放入默认的Properties
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
//从url中解析Properties,并放入默认的Properties
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
//合并configuration中已存在的配置和默认配置,并设置到configuration中
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
5. 解析Setting
private Properties settingsAsProperties(XNode context) {
//为空,返回一个默认全空的Properties
if (context == null) {
return new Properties();
}
//读取所有子节点作为Properties
Properties props = context.getChildrenAsProperties();
//检查所有的配置能被Configuration识别
// Check that all settings 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 setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
6. 根据Setting设置虚拟文件系统VFS
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
//获取虚拟文件的自定义实现类
String value = props.getProperty("vfsImpl");
if (value != null) {
String[] clazzes = value.split(",");
for (String clazz : clazzes) {
if (!clazz.isEmpty()) {
@SuppressWarnings("unchecked")
//反射创建虚拟文件的自定义实现类,并设置到configuration
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
configuration.setVfsImpl(vfsImpl);
}
}
}
}
7.根据Setting设置LogImpl
private void loadCustomLogImpl(Properties props) {
//解析LogImpl
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
//设置LogImpl
configuration.setLogImpl(logImpl);
}
其中resolveClass(props.getProperty(“logImpl”))的底层是调用了TypeAliasRegistry的resolveAlias(String string) 实现
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// issue #748
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
//typeAliases是一个维护了常用的java类的一个Map, 是在TypeAliasRegistry的构造函数中进行的初始化赋值
//使用常用类是不用每次通过反射创建Class对象
if (typeAliases.containsKey(key)) {
value = (Class<T>) typeAliases.get(key);
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
8. 解析别名
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
//通过包名解析类
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
//注册单个别名
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
8.1 通过包名注册别名
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> 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);
}
}
}
ResolverUtil的find()方法
public ResolverUtil<T> find(Test test, String packageName) {
//包名解析为路径名
String path = getPackagePath(packageName);
try {
//通过文件系统获取所有的路径
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
if (child.endsWith(".class")) {
//找到所有.class文件,然后进行匹配,匹配成功加入到内部维护的Set<Class<? extends T>> matches
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
8.2 单个别名的注册
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
如果别名未给定
获取有没有Alias.class注解,如果有注解,则用注解的值作为别名,否则取类型名字作为别名
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
给定别名的情况
如果别名已经被注册并且重新注册的值和之前不同,抛出异常
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 (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
typeAliases.put(key, value);
}
9. 解析自定义拦截器
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//解析所有的interceptor
String interceptor = child.getStringAttribute("interceptor");
//即系interceptor下的属性值
Properties properties = child.getChildrenAsProperties();
//反射获取Interceptor的实例
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
//设置属性
interceptorInstance.setProperties(properties);
//添加interceptor
configuration.addInterceptor(interceptorInstance);
}
}
}
10. 解析对象工厂
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
//解析type
String type = context.getStringAttribute("type");
//解析对象工厂的属性
Properties properties = context.getChildrenAsProperties();
//根据type解析对象工厂类
ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
//设置属性
factory.setProperties(properties);
//设置对象工厂
configuration.setObjectFactory(factory);
}
}
11.解析对象包装工厂
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
//解析type
String type = context.getStringAttribute("type");
//根据type解析对象包装工厂类
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
//设置对象包装工厂
configuration.setObjectWrapperFactory(factory);
}
}
12. 解析反射工厂
private void reflectorFactoryElement(XNode context) throws Exception {
if (context != null) {
//解析type
String type = context.getStringAttribute("type");
//根据type解析反射工厂类
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
//设置对象反射工厂类
configuration.setReflectorFactory(factory);
}
}
13. 设置Configuration的各个属性
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
}
14.设置Environment
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//事务工厂
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//数据源工厂
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
//构建数据源
DataSource dataSource = dsFactory.getDataSource();
//构建Environment
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//设置Environment
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
15. 解析数据库厂商
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
Properties properties = context.getChildrenAsProperties();
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
16. 解析TypeHandler
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//指定包名方式配置
String typeHandlerPackage = child.getStringAttribute("name");
//指定包下进行扫描,找到所有为TypeHandler.class的class,然后进行注册
//会排除接口,匿名内部类,还有抽象类
typeHandlerRegistry.register(typeHandlerPackage);
} else {
//指定单个的处理器
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
17.解析所有的Mapper配置
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//通过包名配置
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//优先从resource进行解析
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//其次从url解析
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//最后从class解析
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.");
}
}
}
}
}
17.1 package 配置 和 class配置
package配置通过扫描包名,找到所有的.class文件,然后调用MapperRegistry的addMapper方法
class配置通过反射获取Mapper类,然后调用MapperRegistry的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 {
//为每个Interface生成一个代理工厂MapperProxyFactory,后续接口的方法执行时获通过代理工厂生成的代理进行执行
//后续章节我们再详细的对MapperProxyFactory进行解析
knownMappers.put(type, new MapperProxyFactory<>(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) {
//如果加载未完成,map中移除该对象
knownMappers.remove(type);
}
}
}
}
17.2 resource 配置和url 配置
都是通过构造XMLMapperBuilder,调用parse()方法进行解析
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//解析mapper文件的各个节点
configurationElement(parser.evalNode("/mapper"));
//加载资源
configuration.addLoadedResource(resource);
//绑定命名空间和Mapper,最终调用MapperRegistry的addMapper方法
bindMapperForNamespace();
}
//解析resultMap
parsePendingResultMaps();
//解析缓存
parsePendingCacheRefs();
//解析语句
parsePendingStatements();
}
总结
通过上述的代码学习,可以发现,大量的工作都是在一开始的xml解析.通过xml解析之后,获取Configuration对象,然后构造SqlSessionFactory.文中并没有对XMLMapperBuilder和MapperAnnotationBuilder中的sql语句解析等进行详细的学习感兴趣的读者可以留言,也可以专门写一遍解析的文章,大家一起学习!
喜欢的小伙伴请动动小手关注和点赞吧,也可留言一起探讨怎样更好的学习源码!!!
文章链接
Mybatis源码学习(一)初探执行流程
Mybatis源码学习(二)配置文件解析到SqlSessionFactory构建
Mybatis源码学习(三)SqlSession详解
Mybatis源码学习(四)自定义Mapper方法执行流程
Mybatis源码学习(五)Executor和StatementHandler详解
Mybatis源码学习(六)结果集自动封装机制
Mybatis源码学习(七)mybatis缓存详解
Mybatis源码学习(八)Mybatis设计模式总结
学习资料整理
本人作为Java开发菜鸡,平时也收集了很多学习视频,在此分享给大家一起学习
整套VIP学习视频
架构师相关视频
扫码领取
更多资料链接
Java免费学习视频下载
Python免费学习视频下载
Web前端免费学习视频下载
人工智能免费学习视频下载
大数据免费学习视频下载
UI设计免费学习视频下载