图片来源 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();
public void parse() { String resource = this.type.toString(); if (!this.configuration.isResourceLoaded(resource)) { this.loadXmlResource(); this.configuration.addLoadedResource(resource); this.assistant.setCurrentNamespace(this.type.getName()); this.parseCache(); this.parseCacheRef(); # 获取Mapper 接口中的所有方法 Method[] methods = this.type.getMethods(); Method[] arr$ = methods; int len$ = methods.length; for(int i$ = 0; i$ < len$; ++i$) { Method method = arr$[i$]; try { if (!method.isBridge()) { # 将 `Mapper.xml` 中每个方法 解析成`MappedStatement`对象 this.parseStatement(method); } } catch (IncompleteElementException var8) { this.configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } this.parsePendingMethods(); }
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; }
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于