SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis
本篇我们在SpringBoot中整合Mybatis这个orm框架,毕竟分析一下其自动配置的源码,我们先来回顾一下以前Spring中是如何整合Mybatis的,大家可以看看我这篇文章Mybaits 源码解析 (十)----- Spring-Mybatis框架使用与源码解析
Spring-Mybatis使用
添加maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
在src/main/resources下添加mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="User" type="com.chenhao.bean.User" />
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins> </configuration>
在src/main/resources/mapper路径下添加User.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.chenhao.mapper.UserMapper">
<select id="getUser" parameterType="int"
resultType="com.chenhao.bean.User">
SELECT *
FROM USER
WHERE id = #{id}
</select>
</mapper>
在src/main/resources/路径下添加beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.chenhao.mapper" />
</bean> </beans>
注解的方式
- 以上分析都是在spring的XML配置文件applicationContext.xml进行配置的,mybatis-spring也提供了基于注解的方式来配置sqlSessionFactory和Mapper接口。
- sqlSessionFactory主要是在@Configuration注解的配置类中使用@Bean注解的名为sqlSessionFactory的方法来配置;
- Mapper接口主要是通过在@Configuration注解的配置类中结合@MapperScan注解来指定需要扫描获取mapper接口的包。
@Configuration
@MapperScan("com.chenhao.mapper")
public class AppConfig { @Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("schema.sql")
.build();
} @Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
} @Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
//创建SqlSessionFactoryBean对象
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
//设置数据源
sessionFactory.setDataSource(dataSource());
//设置Mapper.xml路径
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
// 设置MyBatis分页插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);
sessionFactory.setPlugins(new Interceptor[]{pageInterceptor});
return sessionFactory.getObject();
}
}
最核心的有两点:
- 创建一个SqlSessionFactoryBean,并设置数据源和Mapper.xml路径,其中会解析Mapper.xml文件,最后通过getObject()返回一个SqlSessionFactory 注入Spring容器中
- 通过@MapperScan扫描所有Mapper接口,扫描过程会将Mapper接口生成MapperFactoryBean这个特殊的Bean,并且在其getObject()通过SqlSession().getMapper(this.mapperInterface)生成每个mapper接口真实的代理类
MapperFactoryBean
//最终注入Spring容器的就是这里的返回对象
public T getObject() throws Exception {
//获取父类setSqlSessionFactory方法中创建的SqlSessionTemplate
//通过SqlSessionTemplate获取mapperInterface的代理类
//我们例子中就是通过SqlSessionTemplate获取com.chenhao.mapper.UserMapper的代理类
//获取到Mapper接口的代理类后,就把这个Mapper的代理类对象注入Spring容器
return this.getSqlSession().getMapper(this.mapperInterface);
}
接下来我们看看SpringBoot是如何引入Mybatis的
SpringBoot引入Mybatis
添加mybatis依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
全局配置文件中配置数据源和mybatis属性
spring:
datasource:
url: jdbc:mysql:///springboot
username: root
password: admin
type: com.alibaba.druid.pool.DruidDataSource
initialSize:
minIdle:
maxActive:
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: org.com.cay.spring.boot.entity
加入Mapper扫描注解@MapperScan
@SpringBootApplication
@EnableScheduling
@ServletComponentScan
@MapperScan("com.supplychain.app.mapper")
public class Application { public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
System.setProperty("user.timezone", "GMT+8");
SpringApplication.run(Application.class, args);
}
}
源码解析
mybatis-spring-boot-starter
我们看到mybatis-spring-boot-starter实际上引入了jdbc的场景启动器,这一块我们上一篇文章已经分析过了,还引入了mybatis-spring的依赖,最终还引入了mybatis-spring-boot-autoconfigure这个依赖,其实mybatis-spring-boot-starter只是引入各种需要的依赖,最核心的代码是在引入的mybatis-spring-boot-autoconfigure这个项目当中,我们来看看这个项目
我们看到mybatis-spring-boot-autoconfigure也像spring-boot-autoconfigure一样配置了spring.factories这个配置文件,并且在配置文件中配置了MybatisAutoConfiguration这个自动配置类,我们知道SpringBoot启动时会获取所有spring.factories配置文件中的自动配置类并且进行解析其中的Bean,那么我们就来看看MybatisAutoConfiguration这个自动配置类做了啥?
MybatisAutoConfiguration
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
//引入MybatisProperties配置类
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration { private final MybatisProperties properties; private final Interceptor[] interceptors; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer> configurationCustomizers; public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
} @Bean
@ConditionalOnMissingBean
//往Spring容器中注入SqlSessionFactory对象
//并且设置数据源、MapperLocations(Mapper.xml路径)等
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
//获取SqlSessionFactoryBean的getObject()中的对象注入Spring容器,也就是SqlSessionFactory对象
return factory.getObject();
} @Bean
@ConditionalOnMissingBean
//往Spring容器中注入SqlSessionTemplate对象
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
} //other code...
}
在自动配置的时候会导入一个Properties配置类MybatisProperties,咱们来看一下
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; /**
* Location of MyBatis xml config file.
*/
private String configLocation; /**
* Locations of MyBatis mapper files.
*/
private String[] mapperLocations; /**
* Packages to search type aliases. (Package delimiters are ",; \t\n")
*/
private String typeAliasesPackage; /**
* Packages to search for type handlers. (Package delimiters are ",; \t\n")
*/
private String typeHandlersPackage; /**
* Indicates whether perform presence check of the MyBatis xml config file.
*/
private boolean checkConfigLocation = false; /**
* Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
*/
private ExecutorType executorType; /**
* Externalized properties for MyBatis configuration.
*/
private Properties configurationProperties; /**
* A Configuration object for customize default settings. If {@link #configLocation}
* is specified, this property is not used.
*/
@NestedConfigurationProperty
private Configuration configuration; //other code...
}
该Properties配置类作用主要用于与yml/properties中以mybatis开头的属性进行一一对应,如下
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: org.com.cay.spring.boot.entity
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/cn/mapper/*.xml"></property>
...
</bean>
另外一个Bean为SqlSessionTemplate,通过SqlSessionFactory来生成SqlSession代理类:
public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; //other code... 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; //生成SqlSessioin代理类
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
}
而@MapperScan注解是和Spring整合Mybatis的使用是一样的,都是在配置类上指定Mapper接口的路径,大家可以看一下我以前的一篇文章Mybaits 源码解析 (十一)----- @MapperScan将Mapper接口生成代理注入到Spring-静态代理和动态代理结合使用
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis的更多相关文章
- springboot源码解析-管中窥豹系列之自动装配(九)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanDefine如何加载(十三)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之总体结构(一)
一.简介 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之项目类型(二)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Runner(三)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Initializer(四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之web服务器(七)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanPostProcessor(十二)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之bean如何生成?(十四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
随机推荐
- 百万年薪python之路 -- 推导式
2.1列表推导式 首先我们先看一下这样的代码,给出一个列表,通过循环,想列表中添加1~10: li = [] for i in range(1,11): li.append(i) print(li) ...
- python学习-while True语句
while True是不会跳出循环的. 在while中括号里为一个条件值,只有当条件为真的时候,会执行这条语句,直到条件为false的时候,则会跳出该循环语句.而在这里括号里的值为true,也就意味着 ...
- unity image 设置图片
从任意文件目录下读取文件并在unity中显示: 1)读取目标文件 byte[] imageByte = File.ReadAllBytes(imagePath); 2)转换成纹理 texture.Lo ...
- 条件渲染vue
v-if:只渲染一次的情况下,性能更好v-show:频繁切换性能更好 vue虚拟DOM技术 浏览器:渲染引擎(慢)+JS引擎(快) 用1个JS对象来充当DOM对象,因为JS对象性能比较快,所以用虚拟D ...
- 2018.8.2 python中is和==的区别
一.is 和==的区别 1.is 比较的是左右两边的内存地址, ==比较的是左右两边的值. 2.id() 通过id()可以查看一个变量表示的值得内存中的地址. s = 'alex' s1 = 'ale ...
- MacOS 系统 文件夹解析
Mac OS X,基于UNIX核心的系统,增强了系统的稳定性.性能以及响应能力. 通过对称多处理技术充分发挥双处理器的优势,提供无与伦比的2D.3D和多媒体图形性能以及广泛的字体支持和集成的PDA功能 ...
- 学习笔记32_EF查询优化
*如果有 var temp = from m in dbContext.Model1 where m.属性1 == value select m; foreach(var m1 in temp)//这 ...
- Appium的加载过程
appium运行流程 Appium的加载过程如上图. 1)调用Android adb完成基本的系统操作: 2)向Android上部署bootstrap.jar: 3)Bootstrap.jar For ...
- 和35岁刘阿姨一起自测 Python 流程控制基本功
0.突然降临的困惑 流程控制语句.布尔值.布尔值操作符都属于 python 的基础功.大好周末我写这篇笔记,不是为了整理这些基础知识点,而是记录几个令我突然迷惑的代码段.--代码本身很简单,但即便已经 ...
- [考试反思]0917csp-s模拟测试45:天命
又倒一了. 关于心态,有不少想说的. 首先旁边坐了一个kx.他上来入手T1没多久就切了然后开始对拍拍了几十万组AC. 然而我觉得T1是神仙题.先进T2. 挺简单的,5分钟出正解,然后在打出来的时候突然 ...