java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot自动装配

SpringBoot中的自动装配原理详解

作者:懒惰蜗牛

本文将通过在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文件差异

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

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自动装配的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文