欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-02 12:15:57
本篇我们在SpringBoot中整合Mybatis这个orm框架,毕竟分析一下其自动配置的源码,我们先来回顾一下以前Spring中是如何整合Mybatis的,大家可以看看我这篇文章Mybaits 源码解析 (十) Spring-Mybatis框架使用与源码解析 Spring-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: 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

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

我们看到mybatis-spring-boot-starter实际上引入了jdbc的场景启动器,这一块我们上一篇文章已经分析过了,还引入了mybatis-spring的依赖,最终还引入了mybatis-spring-boot-autoconfigure这个依赖,其实mybatis-spring-boot-starter只是引入各种需要的依赖,最核心的代码是在引入的mybatis-spring-boot-autoconfigure这个项目当中,我们来看看这个项目

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

我们看到mybatis-spring-boot-autoconfigure也像spring-boot-autoconfigure一样配置了spring.factories这个配置文件,并且在配置文件中配置了mybatisautoconfiguration这个自动配置类,我们知道springboot启动时会获取所有spring.factories配置文件中的自动配置类并且进行解析其中的bean,那么我们就来看看mybatisautoconfiguration这个自动配置类做了啥?

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,咱们来看一下

@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
​在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代理类:

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

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。

如果这个文章写得还不错,觉得学到了一点东西的话 求点赞