自动配置过程中做了3个主要bean的创建及很重要的一些事情。
约定大于配置,缺省的配置看这个注解 @EnableConfigurationProperties({MybatisProperties.class})。这个注解中引入了MybatisProperties类,包含了一些默认的配置。
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
}
MybatisAutoConfiguration的主要属性有:
构造方法:
MybatisProperties
?,赋值给 properties 。前提:
正文:
创建一个工厂bean:?MybatisSqlSessionFactoryBean
??
工厂 bean 设置数据源dataSource。依赖注入,此时容器中已经有了dataSource,直接拿来用。
初始化配置属性
通过工厂Bean的getObject方法获取sqlSessionFactory。
对关键配置做断言处理:dataSource
?、sqlSessionFactoryBuilder
?、configuration
?、?configLocation
?
?buildSqlSessionFactory
?:开始构建sqlSessionFactory。
如果配置项 mapperLocation不为空,循环解析每一个 mapper.xml文件
XMLMapperBuilder.parse() 解析xml 文件
如果没有解析过该 xml 文件,则进行解析,如果解析过,直接跳过。
?configurationElement
?????解析mapper 节点
<mapper namespace="com.pansnow.changcheng.mapper.UserMapper">
<select id="queryUserList" resultType="com.pansnow.changcheng.pojo.User">
select *
from user
</select>
<insert id="addUser" parameterType="com.pansnow.changcheng.pojo.User">
insert into user(id, name, pwd)
values (#{id}, #{name}, #{pwd});
</insert>
<update id="updateUser" parameterType="com.pansnow.changcheng.pojo.User">
update user
set name=#{name},
pwd=#{pwd}
where id = #{id};
</update>
<delete id="deleteUser" parameterType="int">
delete
from user
where id = #{id};
</delete>
</mapper>
namespace判空。namespace 为 mapper 接口类的 String 字符串
解析 parameterMap 和 resultMap节点
解析 sql节点
解析 select|insert|update|delete
将该文件添加到已解析集合中。
protected final Set<String> loadedResources;
if (!this.configuration.isResourceLoaded(this.resource)) {
...
this.configuration.addLoadedResource(this.resource);
...
}
将 mapper 类添加到 mapper注册表中。
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
this.knownMappers.put(type, new MapperProxyFactory(type));
knownMapper是一个mapper接口类和他的代理工厂的一个映射。
解析 mapper 类上的注解@Select、@SelectProvider、@ResultMap 等注解,用于解析 mapper 类中方法上的 SQL 语句。
解析方法上的xml文件中的 SQL语句 parseStatement。
MybatisSqlSessionFactoryBean
??中有一个属性mappedStatements
??。这是一个 HashMapMappedStatement
??添加到 map 中使用 jdk 动态代理,创建SqlSessionProxy,增强逻辑为SqlSessionInterceptor
?。代理了SqlSession的创建和获取、事务、关闭。即在调用SqlSession接口中的每一个方法时,都会调用SqlSessionInteceptor的invoke逻辑。
?SqlSessionInterceptor
?的逻辑为:
创建SqlSession。使用 selSessionFactory
代理对象是如何获取defaultSqlSession ,在代理方法中通过SqlSessionUtils 的方法获取SqlSession
- 它会首先获取SqlSessionHolder,SqlSessionHolder用于在TransactionSynchronizationManager中保持当前的SqlSession。
- 如果holder不为空,并且holder被事务锁定,则可以通过holder.getSqlSession()方法,从当前事务中获取sqlSession,即 Fetched SqlSession from current transaction。
- 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。
- 如果当前线程的事务是活跃的,将会为SqlSession注册事务同步,即 Registering transaction synchronization for SqlSession。
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
如果 session 为 null,
TransactionSynchronizationManager
?的synchronizations
?中。synchronizations
?是一个 set 集合。调用业务方法
如果有事务,提交事务
关闭 session。关闭前会释放 holder。?holder.released();
?
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
主要在于根据配置创建MapperScannerConfigurer
?,
@Import({AutoConfiguredMapperScannerRegistrar.class}) 这个AutoConfiguredMapperScannerRegistrar类非常关键,因为他在扫描@Mapper 注解
BeanDefinitionBuilder
?:创建MapperScannerConfigurer
?的 BeanDefinitionBuilderMapperScannerConfigurer
?到注册表中实现了 BeanDefinitionRegistryPostProcessor ,工作在bean 定义的注册阶段。
扫描@Mapper 注解,为什么需要单独处理呢?因为 Mapper 注解是 mybatis 自定义注解,和 Component 注解没有关系。这个类会将@Mapper 注解下的类也实例化后放入到 Spring 容器中。
负责扫描Mapper接口的扫描器为ClassPathMapperScanner
?,他是ClassPathBeanDefinitionScanner
?的子类。ClassPathMapperScanner
?扫描到注解类后,会为其生成BeanDefinition。这里生成的是一个MapperFactoryBean的bean定义。
mapperInterface
? 设置为了 com.pansnow.changcheng.mapper.UserMapperfactoryBeanObjectType
?设置为了 com.pansnow.changcheng.mapper.UserMappersqlSessionTemplate
?的属性,引用指向容器中的bean:sqlSessionTemplate然后在后期创建bean的时候,MapperFactoryBean会执行getObject方法
knownMapper是一个mapper接口类和他的代理工厂的一个映射。
MapperProxyFactory代理工厂创建mapperProxy 代理mapper接口。
this.getSqlSession().getMapper(this.mapperInterface);
||
\||/
this.mapperRegistry.getMapper(type, sqlSession);
||
\||/
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
mapperProxy在执行方法的时候,会从DefaultSqlSession的Configuration中获取方法对应的MappedStatement。接下来就很明白了。
?