Mybati源码解析
Mybatis入口SqlSessionFactoryBuilder
SqlSessionFactoryBuilder是mybatis的入口,它的作用是解析配置文件,创建SqlSessionFactory,然后由SqlSessionFactory创建SqlSession来执行我们的sql。
SqlSessionFactoryBuilder示例代码
//创建SqlSessionFactoryBuilder,用于构建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//读取配置文件构建SqlSessionFactory
SqlSessionFactory sessionFactory = builder.build(new FileInputStream("/mybatis-config.xml"));
//创建SqlSession,用于获取dao接口的代理类或者执行sql
SqlSession sqlSession = sessionFactory.openSession();
//获取dao接口的代理类
MyMapper myMapper = sqlSession.getMapper(MyMapper.class);
//执行接口方法对应的sql
myMapper.getById(1);
这段代码的核心就在build方法中,这个方法会读取配置文件并解析,把解析的配置放在Configuration中,比如plugin,解析好的select,insert,delete,update这些标签的内容,及对应的mapper接口等配置。
解析Config配置文件
下面进入到build方法
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//创建XMLConfigBuilder类,用于解析mybatis配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析方法在parse
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
//把解析配置文件生成的配置类Configuration作为参数,创建DefaultSqlSessionFactory返回
return new DefaultSqlSessionFactory(config);
}
创建解析配置文件类XMLConfigBuilder后,调用parse方法解析配置文件。
public Configuration parse() {
//判断是否解析过了,防止重复解析
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
//标记为已解析
this.parsed = true;
//调用parseConfiguration方法
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
//这里已经把配置文件的内容解析为XNode对象了
try {
//解析properties标签,并把标签的内容放到Configuration中
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins标签,把插件类放到Configuration中
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
//(重点)开始解析我们的mapper文件
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
解析Mapper文件
解析完其它配置后,下面开始解析我们的mapper文件了
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//获取mappers标签的子标签
Iterator var2 = parent.getChildren().iterator();
while(true) {
//遍历子标签
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
//如果子标签是指定的是包名,那么mybatis会把包下所有的接口添加到Configuration,用于生成代理类。
//并解析接口的方法上的注解,例如:@Options,@SelectKey,@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
//子标签是mapper标签,获取mapper标签的resource,url,class属性
//其中resource和url指定的是xml文件, class指定是接口
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
//如果resource不为空,url和mapperClass都为空,那么就创建解析mapper为xml文件的类XMLMapperBuilder
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//如果url不为空,resource和mapperClass都为空,那么就创建解析mapper为xml文件的类XMLMapperBuilder
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
//如果mapperClass不为空,resource和url都为空,说明mapper是接口,就调用解析接口的方法
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
上面是对mapper的配置方式进行判断,如果是xml文件的方式,那么就调用XMLMapperBuilder.parse()进行解析,如果是接口的方式,那么就调用Configuration.addMapper()方法解析。
那么这里我们以解析mapper为xml文件的方式来进行分析。
public void parse() {
//判断mapper文件是否被解析过
if (!this.configuration.isResourceLoaded(this.resource)) {
//没有被解析过,则开始解析
this.configurationElement(this.parser.evalNode("/mapper"));
//把mapper文件添加到Configuration中,表示已解析
this.configuration.addLoadedResource(this.resource);
//把mapper文件的namespace属性的接口添加到Configuration
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
进入到configurationElement方法
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
//解析select,insert,update,delete标签
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
解析select,insert,update,delete标签
buildStatementFromContext()
private void buildStatementFromContext(List<XNode> list) {
if (this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
this.buildStatementFromContext(list, (String)null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
//遍历select,insert,update,delete标签
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
//创建XMLStatementBuilder,用来解析这些标签
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
//解析
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
parseStatementNode()
public void parseStatementNode() {
//获取标签的id属性
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
//获取SqlSource,用于获取sql语句
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String resultType = this.context.getStringAttribute("resultType");
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultSetType = this.context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = this.configuration.getDefaultResultSetType();
}
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String resultSets = this.context.getStringAttribute("resultSets");
//把标签转换成MappedStatement,最后放到Configuration中
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
到这里mybatis的解析阶段就分析完成了。
Mapper接口代理类的生成
在上面的示例代码中,我们只需通过SqlSession.getMapper()方法就能获取到代理类,然后调用代理类的方法就能执行对应的sql,拿到返回值,那么这是怎么实现的呢?
1:先看mapper接口是如何添加到Configuration中的
入口位于Configuration的addMapper方法
public <T> void addMapper(Class<T> type) {
//MapperRegistry是用来保存mapper接口的一个类
this.mapperRegistry.addMapper(type);
}
下面看MapperRegistry的addMapper方法
public <T> void addMapper(Class<T> type) {
//判断参数是否是一个接口
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//把mapper封装为MapperProxyFactory,用于生成代理类,并放到knownMappers这个map中
this.knownMappers.put(type, new MapperProxyFactory(type));
//用于解析接口上的@Select,@Insert,@Update等注解
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
2:如何获取mapper接口及代理类
入口位于SqlSession的getMapper方法
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
紧接着调用了Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
由调了MapperRegistry的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//这里获取了之前保存的MapperProxyFactory
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
//生成代理类
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
生成代理类:位于MapperProxyFactory的newInstance方法
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
到这里代理类生成完成,当我们调用代理类的方法时,就会进入到MapperProxy的invoke方法,然后根据mapper的类名+方法名找到对应的MappedStatement,最后由Executor去执行sql。
关于Environment
在mybatis解析配置文件时,会根据我们对环境的配置进行解析,创建事务工厂(TransactionFactory)和数据源(DataSource)对象,然后放到Environment这个类中,在我们获取SqlSession时会用到。
这个环境对象包含了环境标识id,事物工厂以及数据源。
mybatis支持配置多个不同的环境,比如dev,test,pro环境。
下面是Environment的代码片段
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
} else if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
} else {
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
} else {
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
}
}
}
关于Configuration
Configuration是用来保存解析好的配置信息的,下面是Configuration的代码片段
public class Configuration {
protected Environment environment;
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel;
protected boolean cacheEnabled;
protected boolean callSettersOnNulls;
protected boolean useActualParamName;
protected boolean returnInstanceForEmptyRow;
protected String logPrefix;
protected Class<? extends Log> logImpl;
protected Class<? extends VFS> vfsImpl;
protected LocalCacheScope localCacheScope;
protected JdbcType jdbcTypeForNull;
protected Set<String> lazyLoadTriggerMethods;
protected Integer defaultStatementTimeout;
protected Integer defaultFetchSize;
protected ResultSetType defaultResultSetType;
protected ExecutorType defaultExecutorType;
protected AutoMappingBehavior autoMappingBehavior;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
protected Properties variables;
protected ReflectorFactory reflectorFactory;
protected ObjectFactory objectFactory;
protected ObjectWrapperFactory objectWrapperFactory;
protected boolean lazyLoadingEnabled;
protected ProxyFactory proxyFactory;
protected String databaseId;
protected Class<?> configurationFactory;
//保存mapper接口的类,用于生成代理类
protected final MapperRegistry mapperRegistry;
//拦截器链,用来保存我们配置的插件
protected final InterceptorChain interceptorChain;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final TypeAliasRegistry typeAliasRegistry;
protected final LanguageDriverRegistry languageRegistry;
//mybatis会把解析好的select,insert,delete,update标签封装成MappedStatement,
//然后放到这里,这里key是mapper文件的namespace+标签的id
protected final Map<String, MappedStatement> mappedStatements;
protected final Map<String, Cache> caches;
protected final Map<String, ResultMap> resultMaps;
protected final Map<String, ParameterMap> parameterMaps;
protected final Map<String, KeyGenerator> keyGenerators;
protected final Set<String> loadedResources;
protected final Map<String, XNode> sqlFragments;
protected final Collection<XMLStatementBuilder> incompleteStatements;
protected final Collection<CacheRefResolver> incompleteCacheRefs;
protected final Collection<ResultMapResolver> incompleteResultMaps;
protected final Collection<MethodResolver> incompleteMethods;
protected final Map<String, String> cacheRefMap;
}