SpringBoot中的自动装配原理详解

 更新时间:2024年11月07日 08:20:08   作者:懒惰蜗牛  
本文将通过在Spring中集成MyBatis和在SpringBoot中集成MyBatis为大家简单梳理自动配置过程,感兴趣的小伙伴可以跟随小编一起学习一下

前言

通过两个简单的案例:在Spring中集成MyBatis、在SpringBoot中集成MyBatis

找出两者的差异,初探Spring发展到SpringBoot的部分演化过程

以MyBatis为例,简单梳理自动配置过程

一、Spring整合MyBatis

1.1pom文件

pom.xml

 <!-- Spring JDBC -->
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>${spring.version}</version>
 </dependency>
 ​
 <!-- MyBatis核心包 -->
 <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>3.5.10</version>
 </dependency>
 ​
 <!-- MyBatis-Spring 整合包 -->
 <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis-spring</artifactId>
     <version>2.0.6</version>
 </dependency>
 ​
 <!-- Druid数据库连接池 -->
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.1.23</version>
 </dependency>
 ​
 <!-- MySQL数据库驱动 -->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.26</version>
 </dependency>

1.2配置类

MyBatisConfig

 package com.lazy.snail;
 ​
 import com.alibaba.druid.pool.DruidDataSource;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.mybatis.spring.SqlSessionFactoryBean;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
 ​
 import javax.sql.DataSource;
 ​
 /**
  * @ClassName MyBatisConfig
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/11/5 16:20
  * @Version 1.0
  */
 @Configuration
 @PropertySource("classpath:db.properties")
 @MapperScan("com.lazy.snail.mapper")
 public class MyBatisConfig {
     @Value("${jdbc.url}")
     private String url;
 ​
     @Value("${jdbc.username}")
     private String username;
 ​
     @Value("${jdbc.password}")
     private String password;
 ​
     @Value("${jdbc.driverClassName}")
     private String driverClassName;
 ​
     @Bean
     public DataSource dataSource() {
         DruidDataSource dataSource = new DruidDataSource();
         // 基本配置
         dataSource.setDriverClassName(driverClassName);
         dataSource.setUrl(url);
         dataSource.setUsername(username);
         dataSource.setPassword(password);
 ​
         // Druid 高级配置
 ​
         return dataSource;
     }
 ​
     @Bean
     public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
         SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
         factoryBean.setDataSource(dataSource);
         return factoryBean.getObject();
     }
 }

1.3数据源属性配置文件

 # db.properties
 jdbc.url=jdbc:mysql://*.*.*.*:3306/snail_db
 jdbc.username=username
 jdbc.password=password
 jdbc.driverClassName=com.mysql.cj.jdbc.Driver

1.4mapper

UserMapper

 package com.lazy.snail.mapper;
 ​
 import com.lazy.snail.domain.User;
 import org.apache.ibatis.annotations.Insert;
 import org.springframework.stereotype.Repository;
 ​
 /**
  * @ClassName UserMapper
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/11/5 16:27
  * @Version 1.0
  */
 @Repository
 public interface UserMapper {
     @Insert("insert into user_info(user_id, name, email) values(#{userId}, #{name}, #{email})")
     void insert(User user);
 }

1.5测试类

SpringTest

 package com.lazy.snail;
 ​
 import com.lazy.snail.domain.User;
 import com.lazy.snail.service.UserService;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 ​
 @Slf4j
 public class SpringTest {
 ​
     @Test
     void test() {
         ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         UserService userService = context.getBean(UserService.class);
         userService.createUser(new User(1, "lazysnail", "lazy_snail@aliyun.com"));
     }
 }

二、SpringBoot整合MyBatis

2.1pom文件

pom.xml

 <!-- springboot集成mybatis 自动为mybatis创建和配置所需的bean,简化mybatis的使用 -->
 <dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.2.2</version>
 </dependency>
 ​
 <!-- 数据库连接池 -->
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid-spring-boot-starter</artifactId>
     <version>1.2.8</version>
 </dependency>
 ​
 <!-- 数据库驱动 -->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.33</version>
 </dependency>

2.2配置文件

application.yml

 spring:
   datasource:
     druid:
       url: jdbc:mysql://*.*.*.*:3306/snail_db
       username: username
       password: password
       driver-class-name: com.mysql.cj.jdbc.Driver

2.3mapper

UserMapper

 package com.lazy.snail.mapper;
 ​
 import com.lazy.snail.domain.User;
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
 ​
 /**
  * @ClassName UserMapper
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/10/10 14:03
  * @Version 1.0
  */
 @Mapper
 public interface UserMapper {
     @Insert("insert into user_info(user_id, name, email) values(#{userId}, #{name}, #{email})")
     void insert(User user);
 }

2.4测试类

ApplicationTests

package com.lazy.snail;

import com.lazy.snail.domain.User;
import com.lazy.snail.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    void contextLoads() {
        User user = new User();
        user.setUserId(4);
        userService.createUser(user);
    }
}

三、Spring与SpringBoot整合MyBatis区别

3.1pom文件差异

  • Spring中单独引入功能模块的依赖,如mybatis、mybatis-spring
  • SpringBoot中引入starter概念,需要整合mybatis,只需要引入mybatis-spring-boot-starter依赖

Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.

通过引入 Starter,无需手动查找和复制大量依赖,避免了我们去引入依赖所带来的麻烦

3.2配置类及配置文件差异

Spring中我们需要自定义MyBatis的配置类,专门管理DataSource、SqlSessionFactory、MapperScannerConfigurer这些组件

SpringBoot集成MyBatis时没有任何的相关配置类(基础版),因为SpringBoot提供了自动配置功能,MyBatis 的大部分配置可以通过 mybatis-spring-boot-starter 自动完成,只需要引入相关的依赖,并在 application.propertiesapplication.yml 中进行一些简单配置即可,Spring Boot 会自动创建并配置所需的 Bean(如 SqlSessionFactoryMapperScannerConfigurer 等)。

四、SpringBoot怎么自动配置MyBatis

4.1MyBatis自动配置信息位置

4.2读取自动配置信息

4.3解析bean定义信息

4.3.1解析Application中@Import

/**
 * 激活Spring应用上下问的自动配置功能,尝试猜测和配置需要的bean。
 * 自动配置类通常基于类路径和定义的bean来应用。
 * 例如:
 * 类路径下有tomcat-embedded.jar意味着可能想要一个TomcatServletWebServerFactory的bean
 *(除非自定义了ServletWebServerFactory的bean)
 *
 * 当使用@SpringBootApplication注解时,上下文的自动配置功能自动开启。
 * 添加这个注解没有额外的作用。
 * 
 * 自动配置尽可能的智能,当你自定义了更多配置,自动配置会慢慢弱化。
 * 如果不想用某些配置,你可以通过excludeName手动排除。
 * 也可以通过exclude排除。
 * 自动配置总是在用户定义bean注册之后应用。
 * 
 * 用@EnableAutoConfiguration注解的类包(通常通过@SpringBootApplication)具有特定的意义,通常是默认的。
 * 例如:
 * 它将在扫描@Entity类时使用。
 * 通常建议将@EnableAutoConfiguration(如果不使用@SpringBootApplication)放在根包中,以便可以搜索所有子包和类。
 * 
 * 自动配置类是常规的Spring @Configuration bean。
 * 它们是使用importcandidate和springfactoresloader机制定位的(与这个类相关)。
 * 通常自动配置bean是@Conditional注解的bean(最常使用@ConditionalOnClass和@ConditionalOnMissingBean注解)。
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * 通过class排除不需要的自动配置类
	 */
	Class<?>[] exclude() default {};

	/**
	 * 通过类名称排除不需要的自动配置类
	 */
	String[] excludeName() default {};

}

4.3.1.1获取自动配置入口

// AutoConfigurationImportSelector
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // exclude excludeName
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 所有META-INF/spring.factories中的自动配置类
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去重
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    // 去掉需要排除的
    configurations.removeAll(exclusions);
    // 过滤器过滤
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

4.3.1.2过滤器应用

OnClassCondition过滤器会对配置类中@ConditionalOnClass和@ConditionalOnMissingClass进行匹配过滤

 // MybatisAutoConfiguration
 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })

如果classpath下没有SqlSessionFactory或者SqlSessionFactoryBean,该配置类将被过滤

OnBeanCondition过滤器会对配置类中@ConditionalOnSingleCandidate进行匹配过滤

 // MybatisAutoConfiguration
 @ConditionalOnSingleCandidate(DataSource.class)

应用上下文中是否存在DataSource类型的单个候选 bean。如果存在且只有一个这样的 bean,则条件匹配。

4.3.1.3过滤结果

4.3.1.4遍历处理自动配置类

后续就是将MybatisAutoConfiguration中涉及到的bean定义信息注册到容器中。

五、总结

1. Spring Boot 启动和自动配置的初始化

当 Spring Boot 启动时,SpringApplication.run() 方法会被调用,Spring 容器会加载并处理配置类和自动配置类。在自动配置过程中,Spring Boot 会根据 @EnableAutoConfiguration 注解和 @AutoConfiguration 注解扫描和加载符合条件的自动配置类。

2. 自动配置类的选择

Spring Boot 自动配置是通过 spring.factories 文件来加载的。spring.factories 文件列出了所有自动配置类,在应用启动时,这些自动配置类会被加载到 Spring 容器中。

spring.factories 文件

spring-boot-autoconfigure 模块中,spring.factories 文件中会包含对 MyBatis 自动配置类 MybatisAutoConfiguration 的引用。该文件告诉 Spring Boot 当满足特定条件时,加载 MybatisAutoConfiguration 类。

3. 处理 @EnableAutoConfiguration 和条件注解

Spring Boot 使用了许多条件注解(@Conditional 系列注解),来决定是否启用某些配置类。

@ConditionalOnClass 和 @ConditionalOnMissingClass

MybatisAutoConfiguration 配置类上有 @ConditionalOnClass 注解,表示只有当 SqlSessionFactorySqlSessionFactoryBean 等 MyBatis 相关类在类路径中时,才会启用该自动配置类。@ConditionalOnClassOnClassCondition 过滤器进行检查,判断类路径中是否存在这些类。

MybatisAutoConfiguration 还会使用 @ConditionalOnSingleCandidate 注解来检查 Spring 容器中是否存在且只有一个 DataSource 类型的 Bean。如果条件满足,则加载 MybatisAutoConfiguration

4. 加载 MybatisAutoConfiguration 配置类

在满足了 @ConditionalOnClass@ConditionalOnSingleCandidate 等条件后,MybatisAutoConfiguration 类会被加载并注册到 Spring 容器中。

5. 创建 DataSource 和 SqlSessionFactory

MybatisAutoConfiguration 配置类内部有多个 @Bean 方法,其中主要的两个是 DataSourceSqlSessionFactory 的配置。Spring Boot 会根据现有的条件自动配置这些 Bean。

DataSource

  • 如果你没有手动配置 DataSource,Spring Boot 会使用默认的数据库连接池(如 HikariCP)自动配置一个 DataSource Bean。
  • 如果已经在应用上下文中存在 DataSource,则自动配置会跳过该步骤。

SqlSessionFactory

SqlSessionFactory 是 MyBatis 的核心对象,它需要 DataSource 来进行初始化。MybatisAutoConfiguration 类会检查容器中是否已有 SqlSessionFactory Bean,如果没有,则会创建一个新的 SqlSessionFactory

@ConditionalOnMissingBean 注解表示只有当容器中没有 SqlSessionFactory 时,才会创建这个 Bean。

6. 创建 SqlSessionTemplate

SqlSessionTemplate 是 MyBatis 与 Spring 整合的核心类,负责事务管理和会话的创建。MybatisAutoConfiguration 会根据 SqlSessionFactory 创建一个 SqlSessionTemplate

如果容器中没有 SqlSessionTemplate Bean,MybatisAutoConfiguration 会创建并注册一个 SqlSessionTemplate Bean。

7. 配置 MapperScannerConfigurer 或 @MapperScan

Spring Boot 中的 MyBatis 自动配置已经提供了自动扫描 Mapper 的功能,通常不需要手动配置 MapperScannerConfigurer@MapperScan 注解用于指定扫描 Mapper 接口的包:

Spring Boot 会自动扫描该包下的 Mapper 接口,并将其注册为 Spring Bean。

以上就是SpringBoot中的自动装配原理详解的详细内容,更多关于SpringBoot自动装配的资料请关注脚本之家其它相关文章!

相关文章

  • Java之jdbc连接mysql数据库的方法步骤详解

    Java之jdbc连接mysql数据库的方法步骤详解

    这篇文章主要介绍了Java之jdbc连接mysql数据库的方法步骤详解,JCBC技术是java开发必备的只是,jdbc连接mysql数据库,这是一个比较简单的方法,有兴趣的可以了解一下
    2020-07-07
  • 一文彻底弄懂零拷贝原理以及java实现

    一文彻底弄懂零拷贝原理以及java实现

    零拷贝(英语: Zero-copy) 技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域,下面这篇文章主要给大家介绍了关于零拷贝原理以及java实现的相关资料,需要的朋友可以参考下
    2021-08-08
  • SpringCloudAlibaba分布式配置中心Nacos持久化方式

    SpringCloudAlibaba分布式配置中心Nacos持久化方式

    文章介绍了如何创建Nacos数据库并初始化,修改Nacos配置文件以通过数据库动态修改配置文件,作者分享了遇到的问题和解决方法,提醒在重启服务器时注意MySQL是否已成功启动
    2026-01-01
  • Java静态代理和动态代理的深入讲解

    Java静态代理和动态代理的深入讲解

    这篇文章主要给大家介绍了关于Java静态代理和动态代理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Java基于logback MessageConverter实现日志脱敏方案分析

    Java基于logback MessageConverter实现日志脱敏方案分析

    本文介绍了一种日志脱敏方案,即基于logbackMessageConverter和正则匹配的方法,该方法的优点是侵入性低,工作量少,只需修改xml配置文件,适用于老项目,感兴趣的朋友跟随小编一起看看吧
    2024-10-10
  • Java去掉小数点后面无效0的方案与建议

    Java去掉小数点后面无效0的方案与建议

    当前小数点后面的位数过多的时候,多余的0没有实际意义,下面这篇文章主要给大家介绍了关于Java去掉小数点后面无效0的方案与建议,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Java日常练习题,每天进步一点点(28)

    Java日常练习题,每天进步一点点(28)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-07-07
  • Java反射简易教程

    Java反射简易教程

    这篇文章主要介绍了Java反射简易教程,小编觉得挺不错的,这里分享给大家,需要的朋友可以参考。
    2017-11-11
  • Java注解篇之@SneakyThrows示例详解

    Java注解篇之@SneakyThrows示例详解

    在Java编程中异常处理是一个重要的概念,为了简化异常处理过程,Lombok库提供了一个名为@SneakyThrows的注解,这篇文章主要介绍了Java注解篇之@SneakyThrows的相关资料,需要的朋友可以参考下
    2025-06-06
  • mybatis-plus 批量插入示例代码

    mybatis-plus 批量插入示例代码

    正常我们使用mybatis-plus插入的时候,首先想到的是saveBatch方法,不过看了下打印出来的sql和底层代码,才发现它并不是真正的批量插入这篇文章主要介绍了mybatis-plus 批量插入示例,需要的朋友可以参考下
    2023-07-07

最新评论