https://blog.csdn.net/heroqiang/article/details/79135500
- 整合包版本:1.3.1
- 文章中使用/* */注释的方法会做深入分析做深入分析
正文
首先我们来看Spring整合Mybatis的配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.xxx.entity"/>
<property name="mapperLocations" value="classpath:mapper/* .xml"/>
</bean>
<bean class=“org.mybatis.spring.mapper.MapperScannerConfigurer”>
<property name=“basePackage” value=“com.xxx.dao”/>
<property name=“sqlSessionFactoryBeanName” value=“sqlSessionFactory”/>
</bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
我们就以配置的解析入手,首先我们来看一下SqlSessionFactoryBean的层次结构:
我们发现了两个我们比较关心的接口,FactoryBean和InitializingBean,我们知道,实现了FactoryBean的bean会调用它的getObject方法创建bean,实现了InitializingBean的bean会在属性填充完成之后调用它的afterPropertiesSet方法,我们就来分析这个方法:
SqlSessionFactoryBean:
public void afterPropertiesSet() throws Exception {
// 属性校验
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
/* 构建SqlSessionFactory */
this.sqlSessionFactory = buildSqlSessionFactory();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
SqlSessionFactoryBean:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; // Configuration的几种配置 if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } // objectFactory配置 if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } // objectWrapperFactory配置 if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } // vfs配置 if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } // typeAliasesPackage配置 if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } // typeAliases配置 if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } // plugins配置 if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } // typeHandlersPackage配置 if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } // typeHandlers配置 if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } // databaseIdProvider配置 if (this.databaseIdProvider != null) { try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } // cache配置 if (this.cache != null) { configuration.addCache(this.cache); } if (xmlConfigBuilder != null) { try { // 如果配置了configLocation,则解析配置文件 xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception ex) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); } finally { ErrorContext.instance().reset(); } } if (this.transactionFactory == null) { // 如果没有transactionFactory配置,应用SpringManagedTransactionFactory this.transactionFactory = new SpringManagedTransactionFactory(); } // 为Configuration设置环境 configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); // mapperLocations配置 if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; }
<span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 解析mapper配置</span> XMLMapperBuilder xmlMapperBuilder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLMapperBuilder</span><span class="token punctuation">(</span>mapperLocation<span class="token punctuation">.</span><span class="token function">getInputStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> configuration<span class="token punctuation">,</span> mapperLocation<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> configuration<span class="token punctuation">.</span><span class="token function">getSqlFragments</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> xmlMapperBuilder<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">NestedIOException</span><span class="token punctuation">(</span><span class="token string">"Failed to parse mapping resource: '"</span> <span class="token operator">+</span> mapperLocation <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> ErrorContext<span class="token punctuation">.</span><span class="token function">instance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>LOGGER<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> LOGGER<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Parsed mapper file: '"</span> <span class="token operator">+</span> mapperLocation <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>LOGGER<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> LOGGER<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Property 'mapperLocations' was not specified or no matching resources found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 构建SqlSessionFactory</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>sqlSessionFactoryBuilder<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
熟悉Mybatis的同学一定很容易看懂这个方法,就是对Mybatis的各种配置的设置和解析并构建SqlSessionFactory,不熟悉Mybatis的同学请看笔者关于Mybatis源码解析的文章。方法中有一个步骤是如果用户没有配置transactionFactory,默认将Mybatis的transactionFactory配置为SpringManagedTransactionFactory,我们在分析Mybatis源码的时候看到,在用户没有指定transactionFactory配置的时候,Mybatis使用ManagedTransactionFactory作为默认的TransactionFactory,Mybatis在创建SqlSession时,需要为其添加一个Executor执行器,构建Executor执行器时需要的Transaction对象就是通过TransactionFactory的newTransaction方法创建的,后续Executor执行sql命令时会通过Transaction的getConnection方法获取数据库连接,这里添加的SpringManagedTransactionFactory有什么作用呢?我们可以思考一下,Spring在开启事务的时候需要获取数据库连接,Mybatis执行的时候也要获取数据库连接,在一次调用过程中,两者配合使用时,如果想让Spring的事务作用于Mybatis的数据库操作,那么在这次调用的过程中两者肯定要共用同一个数据库连接,不然事务无法生效,SpringManagedTransactionFactory就可以解决共用数据库连接的问题,我们来分析这个过程:
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
}
- 1
- 2
- 3
SpringManagedTransactionFactory的newTransaction方法会返回SpringManagedTransaction,所以Executor获取数据库连接时就会调用SpringManagedTransaction的getConnection方法:
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection(); /* 开启数据库连接 */
}
return this.connection;
}
- 1
- 2
- 3
- 4
- 5
- 6
SpringManagedTransaction:
private void openConnection() throws SQLException {
/* 获取数据库连接 */
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"JDBC Connection ["
+ this.connection
+ "] will"
+ (this.isConnectionTransactional ? " " : " not ")
+ "be managed by Spring");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
DataSourceUtils:
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
/* 获取数据库连接 */
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
DataSourceUtils:
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
// 从当前线程中获取绑定的ConnectionHolder
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
// 如果ConnectionHolder不为null并且持有数据库连接或者与事务同步
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
// 将ConnectionHolder请求数+1
conHolder.requested();
// 如果ConnectionHolder没有持有数据库连接则获取数据库连接放入ConnectionHolder
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
// 返回数据库连接
return conHolder.getConnection();
}
logger.debug("Fetching JDBC Connection from DataSource");
// 当前线程没有ConnectionHolder,获取数据库连接
Connection con = dataSource.getConnection();
// 判断当前线程的事务同步是否处于活动状态
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// 在事务中使用相同的连接进一步JDBC操作,线程绑定对象将在事务完成时被同步删除
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
// ConnectionHolder为null则创建新的ConnectionHolder
holderToUse = new ConnectionHolder(con);
}
else {
// 不为null则将数据库连接放入ConnectionHolder
holderToUse.setConnection(con);
}
holderToUse.requested(); // 将ConnectionHolder请求数+1
// 为当前线程注册一个新的事务同步
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
// 将SqlSessionHolder标记为与事务同步
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
// 如果是新的ConnectionHolder,则绑定到当前线程
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
这里我们看到通过调用TransactionSynchronizationManager的getResource方法获取当前线程绑定的ConnectionHolder,TransactionSynchronizationManager这个类我们在分析Spring事务源码的时候看到过,用来管理每个线程的资源和事务同步,内部维护了很多ThreadLocal变量来保存一些线程相关的资源。方法中我们发现有一个注册事务同步的过程,这个事务同步是做什么用的呢?我们在分析Spring事务源码的时候提到过,在Spring事务回滚、提交、挂起等操作时会激活事务同步的相关方法,而这里添加的事务同步的作用主要是在Spring事务提交、回滚等操作后将ConnectionHolder的请求数量置为0、将一些属性置为初始状态、将数据库连接放回连接池、释放数据库连接等。
下面我们来分析另外一个bean MapperScannerConfigurer,它的作用是扫描用户配置的包自动注册MapperFactoryBean,这样就不用我们手动注册mapper,我们首先来看一下它的层次结构:
我们发现它同样实现了InitializingBean,但是它对afterPropertiesSet方法的实现仅仅是校验basePackage属性是否为null。我们来看它实现的另外一个接口BeanDefinitionRegistryPostProcessor,这个接口我们在分析Spring上下文初始化源码的时候介绍过,用于在创建bean之前增加或改变BeanDefinition,我们在介绍它的作用时列举的例子就是MapperScannerConfigurer,我们来看相关方法的实现:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
/* 处理properties配置 */
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters(); /* 注册过滤器 */
/* 扫描 */
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里为什么要给出处理properties配置的选项呢?properties配置不是由PropertyPlaceholderConfigurer这个bean来完成的么?(如果读者对PropertyPlaceholderConfigurer不熟悉,可以去任何一个使用Spring的工程中去看一下properties的相关配置,不论是直接配置PropertyPlaceholderConfigurer还是使用<context:property-placeholder/>
标签都与PropertyPlaceholderConfigurer有关)我们尝试翻译一下processPropertyPlaceHolders方法的注释来获得答案:“BeanDefinitionRegistries会在应用启动的时候调用,并且会早于BeanFactoryPostProcessors的调用,这就意味着PropertyResourceConfigurers还没有被加载所以任何对于属性文件的引用将会失效,为了避免这种情况的发生,此方法手动找出定义的PropertyResourceConfigurers并进行提前调用以保证对于属性的引用可以正常工作”。
MapperScannerConfigurer:
private void processPropertyPlaceHolders() {
Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext)
.getBeanFactory().getBeanDefinition(beanName);
// PropertyResourceConfigurer不公开任何显式执行属性占位符替换的方法,代替的是创建一个只包含当前mapper扫描器和后处理工厂的BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition(beanName, mapperScannerBean);
for (PropertyResourceConfigurer prc : prcs.values()) {
// 提前执行PropertyResourceConfigurer的postProcessBeanFactory方法加载properties
prc.postProcessBeanFactory(factory);
}
PropertyValues values = mapperScannerBean.getPropertyValues();
// 更新需要替换的属性
this.basePackage = updatePropertyValue("basePackage", values);
this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
ClassPathMapperScanner:
public void registerFilters() {
boolean acceptAllInterfaces = true;
// 如果指定了annotationClass配置,则添加注解类型过滤器,使用给定的注解和/或标记接口
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// 如果指定了markerInterface配置,则添加可分配给定类型的过滤器,重写AssignableTypeFilter忽略实际标记接口上的匹配
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
// 如果上面两个没有配置,则添加接受所有接口的过滤器
if (acceptAllInterfaces) {
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
// 添加过滤掉package-info.java的过滤器
addExcludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
ClassPathScanningCandidateComponentProvider:
public void addIncludeFilter(TypeFilter includeFilter) {
this.includeFilters.add(includeFilter);
}
- 1
- 2
- 3
ClassPathScanningCandidateComponentProvider:
public void addExcludeFilter(TypeFilter excludeFilter) {
this.excludeFilters.add(0, excludeFilter);
}
- 1
- 2
- 3
添加包括和排除过滤器就是将相关过滤器添加对应集合中,这些过滤器的使用会在稍后进行介绍。相关属性设置完成之后就可以开始进行文件的扫描了:
ClassPathBeanDefinitionScanner:
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages); /* 包扫描 */
if (this.includeAnnotationConfig) {
// 注册注解处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
ClassPathMapperScanner:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类的doScan方法进行扫描
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
/* 处理扫描到的BeanDefinition */
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
扫描的过程其实我们在分析Spring注解扫描的源码的时候已经分析过了,这里的扫描流程同样是复用这个流程,流程中使用的过滤器就是上文中注册的过滤器,这里不再重复这个流程,这里给出文章链接,读者可以去回顾一下这个流程,传送门。下面我们来分析对扫描到的BeanDefinition的处理:
ClassPathMapperScanner:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definiti.. on.getBeanClassName() + "' mapperInterface");
}
// mapper的接口是bean的原始类,但是,实际的bean类是MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
// 下面是一些其他属性的添加
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
我们看到这里为扫描到的BeanDefinition设置了BeanClass为MapperFactoryBean,我们来看一下这个类的层次结构:
我们发现它同样实现了InitializingBean和FactoryBean,我们分析相关实现方法:
DaoSupport:
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
checkDaoConfig(); /* 检查dao配置 */
try {
initDao(); // 子类扩展初始化dao,默认空实现
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
MapperFactoryBean:
protected void checkDaoConfig() {
super.checkDaoConfig(); /* 调用父类方法进行dao配置检查 */
// 检查mapper接口不能为null
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 如果Mybatis的Configuration配置中没有当前mapper,则添加
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
SqlSessionDaoSupport:
protected void checkDaoConfig() {
notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
- 1
- 2
- 3
这里判断了sqlSession属性不能为null,我们在上文中分析处理扫描后的BeanDefinition的过程时,并没有看到为mapper的BeanDefinition增加sqlSession属性,那么MapperFactoryBean创建以后这个属性不就是应该为null么?这里的断言不就无法通过了么?我们知道,SqlSession是通过SqlSessionFactory创建的,处理扫描后的BeanDefinition时为其添加了sqlSessionFactory属性,所以我们尝试从sqlSessionFactory的setter方法中寻找答案:
SqlSessionDaoSupport:
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
/* 创建SqlSessionTemplate */
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
果然,sqlSession属性的赋值就在这。
SqlSessionTemplate:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
/* 调用重载构造方法 */
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
- 1
- 2
- 3
- 4
SqlSessionTemplate:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
/* 调用重载构造方法 */
this(sqlSessionFactory, executorType,
new MyBatisExceptionTranslator(
sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
- 1
- 2
- 3
- 4
- 5
- 6
SqlSessionTemplate:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 创建sqlSession代理
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
我们发现这里创建了一个sqlSession的代理,它有什么用呢?我们知道,我们可以使用SqlSession执行mapper的方法,也可以用它来获取mapper,而SqlSessionTemplate实现了SqlSession并实现了相关方法,方法的功能则委托给了sqlSessionProxy来实现,eg:
SqlSessionTemplate:
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T> selectOne(statement, parameter);
}
- 1
- 2
- 3
动态代理的InvocationHandler角色是SqlSessionInterceptor,我们来看它的invoke方法:
SqlSessionInterceptor:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/* 获取sqlSession */
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行sqlSession的对应方法
Object result = method.invoke(sqlSession, args);
// 如果sqlSession不是由Spring管理的,则提交sqlSession
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// 强制提交sqlSession,因为一些数据库在调用close方法之前需要提交/回滚。
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); // 关闭SqlSession
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); // 关闭SqlSession
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
SqlSessionUtils:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 获取当前线程绑定的SqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
/* 从SqlSessionHolder中获取SqlSession */
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session; // 当前线程存在则直接返回
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
// 没有通过SqlSessionFactory获取新的SqlSession
session = sessionFactory.openSession(executorType);
/* 绑定SessionHolder到当前线程 */
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
SqlSessionUtils:
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
SqlSession session = null;
// 如果SqlSessionHolder不为null并且与事务同步
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
// 将SqlSessionHolder请求数+1
holder.requested();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
}
// 获取SqlSession返回
session = holder.getSqlSession();
}
return session;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
SqlSessionUtils:
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
// 判断当前线程的事务同步是否处于活动状态
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
// 上文创建SqlSessionFactory时判断如果没有transactionFactory配置,应用SpringManagedTransactionFactory
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
}
// 创建SqlSessionHolder持有SqlSession
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
// 绑定到当前线程
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
// 为当前线程注册一个新的事务同步
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
// 将SqlSessionHolder标记为与事务同步
holder.setSynchronizedWithTransaction(true);
// 将SqlSessionHolder请求数+1
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
}
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
这里添加的事务同步的作用主要是在Spring事务提交回滚等操作后调用SqlSession的commit、close等方法来做关闭和提交等操作。
我们继续来分析MapperFactoryBean对FactoryBean接口getObject方法也就是创建bean的方法的实现:
public T getObject() throws Exception {
/* 获取mapper */
return getSqlSession().getMapper(this.mapperInterface);
}
- 1
- 2
- 3
- 4
这里的getSqlSession方法返回的也就是我们上文分析的SqlSessionTemplate。
SqlSessionTemplate:
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
- 1
- 2
- 3
这个方法的内容我们应该很熟悉了,在分析Mybatis源码的时候已经看到过,调用Mybatis的Configuration对象的getMapper方法来获取mapper,不熟悉的同学可以阅读笔者关于Mybatis源码分析的文章,有很详细的说明,同样有mapper执行过程的源码分析。到这里,整个Spring整合Mybatis的源码分析就完成了。
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e44c3c0e64.css" rel="stylesheet">
</div>