图片来源 https://terasolunaorg.github.io,非常推荐此文
org.mybatis-3.2.8
mybatis-spring-1.2.3
mybatis-spring-1.2.xsd
通过上面图片中描述可知,Spring
会扫描 basePackage
下的所有 Mapper
接口并注册为 MapperFactoryBean
MapperFactoryBean
- 上图中可看出,
MapperFactoryBean
实现了 Spring 的FactoryBean
接口,可见MapperFactoryBean
是通过FactoryBean
接口中定义的getObject
方法来获取对应的Mapper
对象(JDK
代理对象)
MapperFactoryBean
还继承了SqlSessionDaoSupport
,需要注入生产SqlSession
的sqlSessionFactory
对象MapperScannerConfigurer
配置中的sqlSessionFactoryBeanName
属性 ,在做多数据源的时候,需要指定不同的SqlSessionFactoryBean
,这样Spring
在注册MapperFactoryBean
的时候会使用不同的SqlSessionFactory
来生成SqlSession
。Spring
整合MyBatis
时,使用的SqlSession
类型是SqlSessionTemplate
。
@Autowired Mapper
# MapperFactoryBean中getObject方法
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
MapperFactoryBean
会从它的getObject
方法中获取对应的Mapper
接口,而getObject
内部还是通过我们注入的属性调用SqlSession
接口的getMapper(Mapper接口)
方法来返回对应的Mapper
接口的代理对象。这样就通过把SqlSessionFactory
和相应的Mapper
接口交给Spring
管理实现了Mybatis
跟Spring
的整合。
追踪一下 getMapper
方法
-
SqlSessionTemplate
......省略 public <T> T getMapper(Class<T> type) { return this.getConfiguration().getMapper(type, this); }
-
Configuration
......省略 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return this.mapperRegistry.getMapper(type, sqlSession); }
-
MapperRegistry
......省略 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 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); } } }
可以看出是从 knownMappers
这个 Map
中取出了 MapperProxyFactory
对象,然后去构建动态代理类,下面先通过 knownMappers
来分析下 MyBatis
的初始化流程。
SqlSessionFactoryBean
SqlSessionFactoryBean
实现了 InitializingBean
接口,在初始化时会执行 afterPropertiesSet
方法。此方法中会 buildSqlSessionFactory
。
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
摘取几段 buildSqlSessionFactory
中的逻辑
......
`xmlConfigBuilder.parse();`
......
`xmlMapperBuilder.parse();`
......
`return this.sqlSessionFactoryBuilder.build(configuration);`
XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
[properties(属性)](http://www.mybatis.org/mybatis-3/zh/configuration.html#properties)
this.propertiesElement(root.evalNode("properties"));
[typeAliases(类型别名)](http://www.mybatis.org/mybatis-3/zh/configuration.html#typeAliases)
this.typeAliasesElement(root.evalNode("typeAliases"));
[plugins(插件)](http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins)
this.pluginElement(root.evalNode("plugins"));
[objectFactory(对象工厂)](http://www.mybatis.org/mybatis-3/zh/configuration.html#objectFactory)
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
[settings(设置)](http://www.mybatis.org/mybatis-3/zh/configuration.html#settings)
this.settingsElement(root.evalNode("settings"));
[environments(环境配置)](http://www.mybatis.org/mybatis-3/zh/configuration.html#environments)
this.environmentsElement(root.evalNode("environments"));
[databaseIdProvider(数据库厂商标识)](http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider)
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
[typeHandlers(类型处理器)](http://www.mybatis.org/mybatis-3/zh/configuration.html#typeHandlers)
this.typeHandlerElement(root.evalNode("typeHandlers"));
[mappers(映射器)](http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers)
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
Mapper 加载的四种方式
-
参看
MyBatis
官网 http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers
-
下面分析
this.mapperElement(root.evalNode("mappers"))
方法,对应上图中的几种加载方式 -
说明:与
Spring
结合,用的是包自动扫描的方式,MyBatis
的全局配置文件中mappers
节点是空的,也就是this.mapperElement(root.evalNode("mappers"))
其实是跳过的,因为root.evalNode("mappers") == NULL
,这个时候用的是buildSqlSessionFactory
方法中xmlMapperBuilder.parse()
来加载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) { `<!-- 使用相对于类路径的资源引用 -->` 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) -->` 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."); } `<!-- 使用映射器接口实现类的完全限定类名 -->` Class<?> mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); }
XMLMapperBuilder
前两种 Mapper
的加载方式,都会调用 mapperParser.parse();
方法,第三种方式是直接加入 Configuration
的配置中 configuration.addMapper(mapperInterface);
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
# Set<String> loadedResources 集合,标识已经加载
this.configuration.addLoadedResource(this.resource);
# 绑定映射器到 namespace
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingChacheRefs();
this.parsePendingStatements();
}
private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
`可以看出最终还是以第三种方式加载 <!-- 使用映射器接口实现类的完全限定类名 -->`
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
}
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
this.configuration.addMapper(boundType);
}
}
}
Configuration.addMapper
Configuration.addMapper
调用的是 mapperRegistry.addMapper
public <T> void addMapper(Class<T> type) {
this.mapperRegistry.addMapper(type);
}
MapperRegistry.addMapper
注册接口,到这里正好和上面 MapperFactoryBean
中 getObject
方法 对应起来了
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
看下在 MapperRegistry addMapper()
方法,里面维护了 knownMappers
,key 为 Class<T> type
,值为 MapperProxyFactory
(创建 Mapper
代理对象的工厂),并且用 MapperAnnotationBuilder
来解析构建 MappedStatement
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 {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
MapperAnnotationBuilder
看下 addMapper
中的 parser.parse();
MapperBuilderAssistant
构建 MappedStatement
对象并加入全局配置 configuration.addMappedStatement(statement);
相对应的 Configuration 中提供了 getMappedStatement(String id)
方法
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) {
if (this.unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
} else {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = new
org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType);
statementBuilder.resource(this.resource);
statementBuilder.fetchSize(fetchSize);
statementBuilder.statementType(statementType);
statementBuilder.keyGenerator(keyGenerator);
statementBuilder.keyProperty(keyProperty);
statementBuilder.keyColumn(keyColumn);
statementBuilder.databaseId(databaseId);
statementBuilder.lang(lang);
statementBuilder.resultOrdered(resultOrdered);
statementBuilder.resulSets(resultSets);
this.setStatementTimeout(timeout, statementBuilder);
this.setStatementParameterMap(parameterMap, parameterType, statementBuilder);
this.setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
this.setStatementCache(isSelect, flushCache, useCache, this.currentCache, statementBuilder);
MappedStatement statement = statementBuilder.build();
this.configuration.addMappedStatement(statement);
return statement;
}
}
MapperProxyFactory
@Autowired
注入的 Mapper
最终会调用 MapperRegistry
的 getMapper(Class<T> type, SqlSession sqlSession)
方法, getMapper
中会通过 knownMappers.get(type)
获取到的 MapperProxyFactory
来构建 JDK
代理对象。
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
MapperProxy
看下代理类的 invoke
方法,是交给了 MapperMethod
来执行。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
🐾 参考
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于