SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis

本篇咱们在SpringBoot中整合Mybatis这个orm框架,毕竟分析一下其自动配置的源码,咱们先来回顾一下之前Spring中是如何整合Mybatis的,你们能够看看我这篇文章Mybaits 源码解析 (十)----- Spring-Mybatis框架使用与源码解析 html

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(); } }

最核心的有两点:java

  • 建立一个SqlSessionFactoryBean,并设置数据源和Mapper.xml路径,其中会解析Mapper.xml文件,最后经过getObject()返回一个SqlSessionFactory 注入Spring容器中
  • 经过@MapperScan扫描全部Mapper接口,扫描过程会将Mapper接口生成MapperFactoryBean这个特殊的Bean,而且在其getObject()经过SqlSession().getMapper(this.mapperInterface)生成每一个mapper接口真实的代理类

MapperFactoryBeanmysql

//最终注入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的git

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: 5 minIdle: 5 maxActive: 20 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这个项目当中,咱们来看看这个项目github

咱们看到mybatis-spring-boot-autoconfigure也像spring-boot-autoconfigure同样配置了spring.factories这个配置文件,而且在配置文件中配置了MybatisAutoConfiguration这个自动配置类,咱们知道SpringBoot启动时会获取全部spring.factories配置文件中的自动配置类而且进行解析其中的Bean,那么咱们就来看看MybatisAutoConfiguration这个自动配置类作了啥?spring

MybatisAutoConfiguration

 1 @org.springframework.context.annotation.Configuration  2 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })  3 @ConditionalOnBean(DataSource.class)  4 //引入MybatisProperties配置类  5 @EnableConfigurationProperties(MybatisProperties.class)  6 @AutoConfigureAfter(DataSourceAutoConfiguration.class)  7 public class MybatisAutoConfiguration {  8 
 9     private final MybatisProperties properties; 10 
11     private final Interceptor[] interceptors; 12 
13     private final ResourceLoader resourceLoader; 14 
15     private final DatabaseIdProvider databaseIdProvider; 16 
17     private final List<ConfigurationCustomizer> configurationCustomizers; 18 
19     public MybatisAutoConfiguration(MybatisProperties properties, 20                                     ObjectProvider<Interceptor[]> interceptorsProvider, 21  ResourceLoader resourceLoader, 22                                     ObjectProvider<DatabaseIdProvider> databaseIdProvider, 23                                     ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { 24         this.properties = properties; 25         this.interceptors = interceptorsProvider.getIfAvailable(); 26         this.resourceLoader = resourceLoader; 27         this.databaseIdProvider = databaseIdProvider.getIfAvailable(); 28         this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(); 29  } 30 
31  @Bean 32  @ConditionalOnMissingBean 33     //往Spring容器中注入SqlSessionFactory对象 34 //而且设置数据源、MapperLocations(Mapper.xml路径)等
35     public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { 36         SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); 37  factory.setDataSource(dataSource); 38         factory.setVfs(SpringBootVFS.class); 39         if (StringUtils.hasText(this.properties.getConfigLocation())) { 40             factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); 41  } 42         Configuration configuration = this.properties.getConfiguration(); 43         if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { 44             configuration = new Configuration(); 45  } 46         if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { 47             for (ConfigurationCustomizer customizer : this.configurationCustomizers) { 48  customizer.customize(configuration); 49  } 50  } 51  factory.setConfiguration(configuration); 52         if (this.properties.getConfigurationProperties() != null) { 53             factory.setConfigurationProperties(this.properties.getConfigurationProperties()); 54  } 55         if (!ObjectUtils.isEmpty(this.interceptors)) { 56             factory.setPlugins(this.interceptors); 57  } 58         if (this.databaseIdProvider != null) { 59             factory.setDatabaseIdProvider(this.databaseIdProvider); 60  } 61         if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { 62             factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); 63  } 64         if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { 65             factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); 66  } 67         if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { 68          factory.setMapperLocations(this.properties.resolveMapperLocations()); 69  } 70        //获取SqlSessionFactoryBean的getObject()中的对象注入Spring容器,也就是SqlSessionFactory对象 71 return factory.getObject(); 72  } 73 
74  @Bean 75  @ConditionalOnMissingBean 76     //往Spring容器中注入SqlSessionTemplate对象
77     public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { 78         ExecutorType executorType = this.properties.getExecutorType(); 79         if (executorType != null) { 80            return new SqlSessionTemplate(sqlSessionFactory, executorType); 81         } else { 82             return new SqlSessionTemplate(sqlSessionFactory); 83  } 84  } 85     
86     //other code...
87 }

在自动配置的时候会导入一个Properties配置类MybatisProperties,我们来看一下sql

@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开头的属性进行一一对应,以下springboot

mybatis: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml type-aliases-package: org.com.cay.spring.boot.entity
​在 MybatisAutoConfiguration自动配置类中,SpringBoot默认自动配置了两个Bean,分别是 SqlSessionFactorySqlSessionTemplate。咱们看到上面代码中第71行,实际上是返回的factory.getObject();,也就是注入Spring容器中的是SqlSessionFactory对象,SqlSessionFactory主要是将Properties配置类中的属性赋值到SqlSessionFactoryBean中,相似之前xml中配置的SqlSessionFactory
<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代理类:session

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-静态代理和动态代理结合使用mybatis

 

原文出处:https://www.cnblogs.com/java-chen-hao/p/11849869.html

相关文章
相关标签/搜索