当前位置: 首页 > news >正文

聊一聊SpringBoot的自动装配原理

前言


通过两个简单的案例:在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;@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();// 基本配置dataSource.setDriverClassName(driverClassName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);// Druid 高级配置return dataSource;}@Beanpublic 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 {@Testvoid 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_dbusername: usernamepassword: passworddriver-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 {@Autowiredprivate UserService userService;@Testvoid 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依赖

[!NOTE]

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自动配置信息位置

image-20241106114722815

4.2读取自动配置信息

image-20241106135214457

4.3解析bean定义信息

4.3.1解析Application中@Import

image-20241106141046287

/*** 激活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 excludeNameAnnotationAttributes 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过滤器应用

image-20241106143326101

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

    1. // MybatisAutoConfiguration
      @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
      
    2. 如果classpath下没有SqlSessionFactory或者SqlSessionFactoryBean,该配置类将被过滤

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

    1. // MybatisAutoConfiguration
      @ConditionalOnSingleCandidate(DataSource.class)
      
    2. 应用上下文中是否存在DataSource类型的单个候选 bean。如果存在且只有一个这样的 bean,则条件匹配。

4.3.1.3过滤结果

image-20241106162130833

4.3.1.4遍历处理自动配置类

image-20241106162719255

后续就是将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. 创建 DataSourceSqlSessionFactory

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。


http://www.mrgr.cn/news/66980.html

相关文章:

  • Vue2+3
  • 配电柜防凝露装置功能参数介绍
  • C#实现傅里叶变换算法
  • 基于LabVIEW应用ARINC 429板卡实现数据通讯——(下篇)
  • clang-tidy 检查清单 学习笔记2
  • 房屋租赁管理系统的费用是多少?
  • 去除人声的利器:消音伴奏软件合集
  • AB 罗克韦尔模块 SD3K2004K
  • img图片为null或错误时替换为静态图片
  • 项目范围产品范围
  • C++ 项目中使用 .dll 和 .def 文件的操作指南
  • watch与computed的区别、运用的场景
  • PCIe板卡的标准尺寸介绍
  • 7篇Python爬虫实例,直接代码可运行,全网最全,注释超详细(适合收藏)——2、爬取图片信息。
  • Pimpl(Pointer to Implementation)模式详解
  • PMP--入栏需看
  • C++:多态中的虚/纯虚函数,抽象类以及虚函数表
  • 逻辑漏洞验证码识别
  • 2024中国国际数字经济博览会:图为科技携明星产品引领数智化潮流
  • AXI总线上的大小端
  • Python 爬虫:从入门到精通有这一篇文章就够了
  • 雷池社区版 7.1.0 LTS 发布了
  • JAVA开发支付(工作中学到的)
  • ssm+vue683基于VUE.js的在线教育系统设计与实现
  • 短视频矩阵系统的源码, OEM贴牌源码
  • 微服务系列四:热更新措施与配置共享