工作原理
MyBatis通过解析mybatis-config.xml和Mapper.xml将所有的配置信息都保存在配置类Configuration中(其中XMLConfigBuilder负责解析mybatis-config.xml、XMLMapperBuilder负责解析Mapper.xml),并使用配置类Configuration创建SqlSessionFactory对象,然后通过SqlSessionFactory创建SqlSession对象,最终使用SqlSession创建Mapper代理对象MapperProxy,Mapper接口的所有方法都是由MapperProxy负责执行的。
整体流程
示例代码
public static void main(String[] args) throws IOException {
//1.读取mybatis-config.xml 文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory(建造者设计模式)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.打开SqlSession
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
//4.JDK动态代理获取Mapper接口对象
StDataMapper mapper = sqlSession.getMapper(StDataMapper.class);
List<StData> resultList = mapper.findAll();
for (StData item : resultList) {
System.out.println("item:" + item.toString());
}
}
}
源码分析
1. 解析配置文件mybatis-config.xml
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder类的build方法负责创建SqlSessionFactory对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//使用XMLConfigBuilder解析mybatis-config.xml
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//将所有配置信息都封装在Configuration类中,并使用其创建SqlSessionFactory类
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
XMLConfigBuilder 对象继承 BaseBuider 抽象类,创建XMLConfigBuilder对象时会实例化配置类Configuration
private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
Properties props) {
//创建配置类Configuration,并封装在XMLConfigBuilder
super(newConfig(configClass));
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
private static Configuration newConfig(Class<? extends Configuration> configClass) {
try {
return configClass.getDeclaredConstructor().newInstance();
} catch (Exception ex) {
throw new BuilderException("Failed to create a new Configuration instance.", ex);
}
}
其parse方法用来解析 mybatis-config.xml 配置文件,并将配置信息保存在配置类Configuration中
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//重点!解析mybatis-config.xml,将配置信息封装在Configuration
parseConfiguration(parser.evalNode("/configuration"));
//返回配置类Configuration,用于创建SqlSessionFactory
return configuration;
}
parseConfiguration方法负责解析mybatis-config.xml配置中各个节点
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
//1.解析<properties>节点
propertiesElement(root.evalNode("properties"));
//2.解析<settings>节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//3.解析<typeAliases>节点
typeAliasesElement(root.evalNode("typeAliases"));
//4.解析<plugins>节点
pluginElement(root.evalNode("plugins"));
//5.解析<objectFactory>节点
objectFactoryElement(root.evalNode("objectFactory"));
//6.解析<objectWrapperFactory>节点
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//7.解析<reflectorFactory>节点
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//8.解析<environments>节点
environmentsElement(root.evalNode("environments"));
//9.解析<databaseIdProvider>节点
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//10.解析<typeHandlers>节点
typeHandlerElement(root.evalNode("typeHandlers"));
//11.解析<mappers>节点 重点!!!
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
mapperElement方法负责解析mybatis-config.xml中的mappers节点,MyBatis支持4种Mapper注册方式,这在mapperElement方法中可以得到验证。
<mappers>
<!-- 通过package元素将指定包下面的所有Mapper接口进行注册 -->
<package name="com.izx.mapper"/>
<!-- 通过mapper元素的resource属性指定一个相对于类路径的Mapper.xml文件 -->
<mapper resource="com/izx/mapper/StDataMapper.xml"/>
<!-- 通过mapper元素的url属性指定一个通过URL请求道的Mapper.xml文件 -->
<mapper url="file:com/izx/mapper/StDataMapper.xml"/>
<!-- 通过mapper元素的class属性指定一个Mapper接口进行注册 -->
<mapper class="com.izx.mapper.StDataMapper"/>
</mappers>
下面将重点分析通过package元素注册指定包下的所有Mapper接口的方式,对于mapperElement方法有两个作用:
- 将Mapper接口至Mapper代理工厂MapperProxyFactory的映射添加至Mapper注册器MapperRegistry的 knownMappers集合中(knownMappers是Map<Class<?>, MapperProxyFactory<?>>集合)。
- 解析mapper.xml,创建对应的MappedStatement,将其保存在Configration类的mappedStatements集合中
//重点分析通过package元素注册指定包下的所有Mapper接口
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//1.通过package元素将指定包下面的所有Mapper接口进行注册
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
//重点!
//将Mapper接口映射添加至Mapper注册器MapperRegistry
//解析mapper.xml创建MappedStatement,保存至Configuration.mappedStatements
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) {
//2.通过mapper元素的resource属性指定一个相对于类路径的Mapper.xml文件
ErrorContext.instance().resource(resource);
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
//3.通过mapper元素的url属性指定一个通过URL请求获取的Mapper.xml文件
ErrorContext.instance().resource(url);
try (InputStream inputStream = Resources.getUrlAsStream(url)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url,
configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
//4.通过mapper元素的class属性指定一个Mapper接口进行注册
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.");
}
}
}
}
}
通过package元素注册Mapper接口时使用configuration.addMappers方法将Mapper接口注册工作委托给MapperRegistry
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
1.1. Mapper注册器MapperRegistry
MapperRegistry有两个主要属性:config和KnownMappers
- config: MyBatis全局配置类Configuration
- KnownMappers: Map<Class<?>, MapperProxyFactory<?>>类型的集合,实现Mapper类到MapperProxyFactory的映射,将Mapper类添加至集合后必须完成一次Mapper.xml配置解析,如果解析不成功,那么仍然会将mapper接口移除
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
//注册Mapper接口
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 {
//添加Mapper接口至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.
//重点! 解析mapper.xml生成MappedStatement,保存至Configuration
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
//解析mapper.xml失败时,会将Mapper接口从knownMappers中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2. 解析一系列Mapper.xml配置文件
2.1. MapperAnnotationBuilder类
MapperAnnotationBuilder的parse方法将会加载mapper.xml配置文件,并使用XMLMapperBuilder解析mapper.xml
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//加载mapper.xml,并使用XMLMapperBuilder解析
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
for (Method method : type.getMethods()) {
if (!canHaveStatement(method)) {
continue;
}
if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
&& method.getAnnotation(ResultMap.class) == null) {
parseResultMap(method);
}
try {
parseStatement(method);
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
loadXmlResource()方法负责加载并解析mapper.xml
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347 1.从src目录下加载mapper.xml
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
//2.src目录下不存在,从resource目录下加载mapper.xml
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
//解析mapper.xml,创建一组MappedStatement,保存在Configuration的mappedStatements
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource,
configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
2.2. MappedStatement类
MappedStatement 类存储了一个 sql 语句的所有信息,Mybatis 通过解析mapper.xml文件 和 mapper 接口上的注解,生成 sql 对应的 MappedStatement 实例, 保存在Configuration 类属性mappedStatements中。
public class Configuration {
//StrictMap继承自ConcurrentHashMap
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
"Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
+ targetValue.getResource());
}
真正执行 mapper 接口中的方法时,会从 Configuration 中找到对应的 MappedStatement,然后进行后续的操作。
3.创建Mapper代理对象MapperProxy
//3.打开SqlSession
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
//4.JDK动态代理获取Mapper接口对象
StDataMapper mapper = sqlSession.getMapper(StDataMapper.class);
List<StData> resultList = mapper.findAll();
for (StData item : resultList) {
System.out.println("item:" + item.toString());
}
}
SqlSession.getMapper(StDataMapper.class)方法内部调用configuration.getMapper()方法,最终调用mapperRegistry.getMapper()方法
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
}
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//获取Mapper对象代理类MapperProxy
return mapperRegistry.getMapper(type, sqlSession);
}
}
MapperRegistry的getMapper方法使用MapperProxyFactory类创建Mapper代理对象
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//上面解析mappers节点时,创建Mapper类至MapperProxyFactory的映射
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//使用JDK动态代理创建MapperProxy代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory使用JDK动态代理生成Mapper代理对象MapperProxy
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//JDK动态代理创建MapperProxy,Mapper接口的方法都由MapperProxy负责执行
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
4.Mapper代理对象MapperProxy
MapperProxy实现了InvocationHandler接口,负责执行Mapper接口定义的方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
//代理过的方法会缓存在methodCache中
private final Map<Method, MapperMethodInvoker> methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//Object中定义的方法直接执行,如toString(),hashCode()等
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//其他Mapper接口定义的方法交由MapperMethodInvoker来执行
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
//如果method方法已经代理过则从methodCache直接获取,否则创建MethodInvoker并缓存至methodCache
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (!m.isDefault()) {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
}
下一篇将会分析MapperMethod具体执行Sql语句流程,并引入MyBatis的四大对象
- 执行器Executor
- JDBC Statement处理类StatementHandler
- SQL参数处理类ParameterHandler
- 查询结果集处理类ResultSetHandler