一、对源码先上一个结构图:

源代码主要在org.apache.ibatis目录下,18个包,其中在应用中主要的包有:builder、session、cache、type、transaction、datasource、jdbc、mapping,提供支撑服务的包有annotation、binding、io、logging、plugin、reflection、scripting、exception、executor、parsing
二、从使用入手
MyBatis使用的三板斧是SqlSessionFactoryBuilder和SqlSessionFactory、SqlSession
-
SqlSessionFactoryBuilder
支持9种构造方法,其实最主要的是包含Configuration对象的构造方法,目的是为了通过加载配置文件创造SqlSessionFactory对象,真实最终返回的是DefaultSqlSessionFactory对象

所有的构造方法最终都是调用build(Configuratiron)方法,这就要来研究一下Configuration对象,其实他就是对xml配置文件的对象映射,关于xml文件结构组成可从源码中看出如下:
本文介绍一下Configuration的大框架,后续开个专辑专门研究Configuration的细节
propertiesElement(root.evalNode("properties")); //加载资源文件属性和当前文件属性 typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
环境元素(数据源和事务)、 属性、类型别名、typeHandler、mapper、setting、插件
-
属性:
先来看属性的加载,属性的加载最重要的是了解三种属性来源(属性配置文件、当前文件的属性、java代码输入)和三种属性的加载顺序(先加载配置文件,在加载config文件的属性,最后加载java代码输入)
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
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) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
-
类型别名
主要是类的完整路径和简单别名的对应关系加载,保存在容器typeAliasRegistry中,最终映射到configuratiron对象中
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);
}
}
}
}
}
-
typeHandler
用户自定义类型处理器,保存在typeHandlerRegistry容器中,最终保存在configuration对象中
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
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);
}
}
}
}
}
-
mapper
主要是加载mapper.xml文件,同构mapperParser对文件进行解析,保存在容器mapperRegistry中,最终保存到configuration对象中
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");
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) {
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<?> 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.");
}
}
}
}
}
-
环境元素
主要加载数据源、和事务配置信息,由Environment.Builder对象进行处理,并将信息保存到configuratiron对象
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.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
-
setting
加载ibatis自身工作需要配置的所有设置信息,为configuration对象进行赋值
1 private void settingsElement(XNode context) throws Exception { 2 3 if (context != null) { 4 5 Properties props = context.getChildrenAsProperties(); 6 7 // Check that all settings are known to the configuration class 8 9 MetaClass metaConfig = MetaClass.forClass(Configuration.class); 10 11 for (Object key : props.keySet()) { 12 13 if (!metaConfig.hasSetter(String.valueOf(key))) { 14 15 throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); 16 17 } 18 19 } 20 21 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); 22 23 configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); 24 25 configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); 26 27 configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); 28 29 configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); 30 31 configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); 32 33 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); 34 35 configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); 36 37 configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); 38 39 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); 40 41 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); 42 43 configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); 44 45 configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); 46 47 configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); 48 49 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); 50 51 configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); 52 53 configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); 54 55 configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); 56 57 configuration.setLogPrefix(props.getProperty("logPrefix")); 58 59 configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); 60 61 configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); 62 63 configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty("injectionFilterEnabled"), false)); 64 65 configuration.setInjectionFilter(parseExpression(props.getProperty("injectionFilter"), "^[a-zA-Z0-9._]*$")); 66 67 } 68 69 }
2、SqlSessionFactory,真实干活的DefaultSqlSessionFactory
SqlSessionFactory的作用是承上启下,作为SqlSession的工厂,主要是工作必须是提供获取SqlSession的方法,同时还提供了一个获取Configuration的方法
其中SqlSessionFactory提供了8种获取SqlSession的方法,主要涉及4个参数:是否自动提交、事务级别、ExecutorType(Statement类型【普通、预处理、批处理】)、自定义Connection
其中需要注意的是openSession()方法默认不是自动commit的。
1 SqlSession openSession(); 2 SqlSession openSession(boolean autoCommit); 3 SqlSession openSession(Connection connection); 4 SqlSession openSession(TransactionIsolationLevel level); 5 SqlSession openSession(ExecutorType execType); 6 SqlSession openSession(ExecutorType execType, boolean autoCommit); 7 SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); 8 SqlSession openSession(ExecutorType execType, Connection connection); 9 Configuration getConfiguration();
揭开如何提供SqlSession的秘密(在这里发现了Configuration还可以提供创造Executor的工作,Connection被封装在Transaction 中,Configuration做了两件事情,一个是配置信息存储,还有内部对象的组装工作,需要深入研究这种模式是否可以通过另一个Action对象进行管理呢?)
1private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 2 Transaction tx = null; 3 try { 4 final Environment environment = configuration.getEnvironment(); 5 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); 6 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 7 final Executor executor = configuration.newExecutor(tx, execType); 8 return new DefaultSqlSession(configuration, executor, autoCommit); 9 } catch (Exception e) { 10 closeTransaction(tx); // may have fetched a connection so lets call close() 11 throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); 12 } finally { 13 ErrorContext.instance().reset(); 14 } 15 } 16 private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) { 17 try { 18 boolean autoCommit; 19 try { 20 autoCommit = connection.getAutoCommit(); 21 } catch (SQLException e) { 22 // Failover to true, as most poor drivers 23 // or databases won't support transactions 24 autoCommit = true; 25 } 26 final Environment environment = configuration.getEnvironment(); 27 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); 28 final Transaction tx = transactionFactory.newTransaction(connection); 29 final Executor executor = configuration.newExecutor(tx, execType); 30 return new DefaultSqlSession(configuration, executor, autoCommit); 31 } catch (Exception e) { 32 throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); 33 } finally { 34 ErrorContext.instance().reset(); 35 } 36 }
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
这个需要重点关注。创建事务,将Connection传递给tx的过程。
3、SqlSession,真实干活的是DefaultSqlSession
SqlSession类主要封装了Configuration对象、Executor对象、是否自动提交
SqlSession是在程序中真实干活的人,我们在使用Mybatis中打交道最频繁的就是SqlSessinon对象,上个结构图,看看他都在干啥

看到图了,就没有什么秘密了,他就是在干数据的日常操作的活,只不过是用他自己封装的一套东西,比如说Configuration(封装Connection【来源可以使Pool】)、Executor(封装Statement)、ResultHandler(封装处理ResultSet对象)、RowBounds(封装分页对象),提供了CRUD,提供了缓存机制,提供了根据配置文件获取Sql语句的方法,提供了事务的提交和回滚等。