搞定SpringBoot的自动注入原理,一篇文章就够了

本文详细介绍如何自定义Spring Boot Starter,包括创建过程、自动配置原理及如何利用配置文件注入属性值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

使用 springboot 进行项目开发时,无需各种配置文件,无需繁杂的 pom 坐标,只要一个 xxxApplication (启动类)就 run 起来了,那 springboot 是怎么做到约定大于配置的?

自定义 starter

为什么要自定义 starter

在日常开发中,经常会有一些独立于业务的公共模块,如果多个工程中都可以复用这个公共模块的话,不需要手动拷贝到工程中,我们将公共的模块封装成一个个starter,复用的时候直接引入依赖即可,springboot为我们完成自动装配。

命名规则

springboot官方提供的 starter 以 spring-boot-starter-xxx 方式命名。官方建议自定义的 starter 使用 xxx-spring-boot-starter 的方式命名,以区分这两种 starter

自定义 starter 实现

1、新建空 project ,名为:hello-spring-boot-starter
2、新建子 modules,名为:hello-spring-boot-autoconfigure
3、hello-spring-boot-autoconfigure 中 pom引入依赖

<!--自动配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

<!--支持读取xml/properties文件配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

4、新建 HelloProperties 类

// 配置属性前缀
@ConfigurationProperties("my.hello")
public class HelloProperties {
    private String name;
    private Integer age;
    private String hometown;
    // getter setter toString 略
}

5、新建 HelloService 类

public class HelloService {
    private String name;
    private Integer age;
    private String hometown;

    public HelloService(String name, Integer age, String hometown) {
        this.name = name;
        this.age = age;
        this.hometown = hometown;
    }

    public String sayHello(String name) {
        return "hello..." + name;
    }

    public String helloWorld() {
        return String.format("[name=%s, age=%d, hometown=%s]", this.name, this.age, this.hometown);
    }
}

6、新建 HelloServiceAutoConfiguration 类

@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    private final HelloProperties helloProperties;

    public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    @Bean
    @ConditionalOnMissingBean		// HelloService 类不存在时执行此方法
    public HelloService helloService() {
        return new HelloService(this.helloProperties.getName(), this.helloProperties.getAge(), this.helloProperties.getHometown());
    }
}

7、类路径下新建 META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.springboot.HelloServiceAutoConfiguration

8、modules mvn clean install ,将子modules打包到本地仓库

9、在父project中引入子 modules 的依赖

<dependencies>
    <dependency>
        <groupId>com.springboot</groupId>
        <artifactId>hello-spring-boot-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

10、此时,自定义starter已经创建完成了,新建子modules ,引入此starter测试
11、spring-boot-test 中引入依赖

<!--自定义starter-->
<dependency>
   <groupId>com.springboot</groupId>
   <artifactId>hello-spring-boot-autoconfigure</artifactId>
   <version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

12、新建 HelloController 类

@RestController
@RequestMapping("/hello")
public class HelloController {
    @Resource
    private HelloService helloService;

    @GetMapping("/{name}")
    public String hello(@PathVariable String name) {
        return helloService.sayHello(name);
    }
    
	@GetMapping
    public String helloWorld() {
        return helloService.helloWorld();
    }
}

13、application.properties 添加配置信息

my.hello.name=feiyangyang
my.hello.age=19
my.hello.hometown=bj

14、启动项目,测试


自动配置原理

springboot核心注解 @SpringBootApplication 是个组合注解,其中包括三个重要注解
在这里插入图片描述

@SpringBootConfiguration

在这里插入图片描述

我们可以看到,@SpringBootConfiguration 注解继承自 @Configuration,都用来声明当前类是个配置类。并且会将当前类声明的一个或多个以 @Bean 注解标记的实例注入 IOC 容器中。

在这里插入图片描述

并且,Spring 的配置类也是 IOC 容器的组件。


@EnableAutoConfiguration

在这里插入图片描述

@EnableAutoConfiguration 注解是 springboot 实现自动化配置的核心注解,就是通过这个注解把 spring 应用所需的 bean 注入到容器中。

在这里插入图片描述
再来看看 @AutoConfigurationPackage 注解,自动配置包,主要是使用 @Import 来给 spring 容器导入一个组件,这里导入的是 AutoConfigurationPackages.Registrar.class

在这里插入图片描述

我们来看 AutoConfigurationPackages.Registrar.class ,发现是 AutoconfigurationPackages 类的内部类。通过分析 AutoConfigurationPackages.Registrar 类的 registerBeanDefinitions 方法,发现其内部调用的 register 方法的参数是 PackageImports 类的实例,我们来到 PackageImports 类的构造,发现此构造就是拿到所有要扫描的包路径。

在这里插入图片描述
我们打断点debug,发现拿到的包路径正是我们项目的包路径。
在这里插入图片描述在这里插入图片描述
而 PackageImports 构造的参数 AnnotationMetadata 是什么东西?发现其实就是项目的启动类,看 PackageImports 构造发现,不过就是拿到了启动类所在的包路径,获取到了包路径,springboot 会将这个包路径下的组件扫描到 IOC 容器。
在这里插入图片描述

我们回到 @EnableAutoConfiguration 看看 @Import 上面标注的类是做什么的。

在这里插入图片描述
我们点到 AutoConfigurationImportSelector 类发现有一个 selectImports 方法,而这个方法做的就是将所有组件注入到 IOC 容器中,并且将这些组件的全限定类名返回,我们来验证一下。

在这里插入图片描述

selectImports() 方法有个 getAutoConfigurationEntry() ,发现getAutoConfigurationEntry()拿到了很多 自动配置类 (xxxAutoConfiguration) ,将这些自动配置类注入到 IOC 容器。那 springboot 是怎么拿到这些 xxxAutoConfiguration 类的?

在这里插入图片描述
在这里插入图片描述
最终 debug 来到 loadSpringFactories() 方法,发现扫描的是 META-INF/spring.factories 文件。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
将META-INF/spring.factories文件中的内容用 ConcurrentReferenceHashMap 存储起来,此ConcurrentReferenceHashMap 是个Map<ClassLoader, Map<String, List>>这样的结构,其中包括了类加载器,以 META-INF/spring.factories 文件中属性名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为 key,所有属性值作为value 的形式存储起来。

稍微总结一下,springboot 在启动的时候从类路径下的 META-INF/spring.factories 文件中获取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 指定的值,将这些值作为自动配置类导入到容器中,自动配置类就会生效,帮我们进行自动配置工作,以前我们需要自己配置的东西,自动配置类就帮我们完成了。

springboot自动配置的原理:从 classpath 中搜寻所有的 META-INF/spring.factories 文件,并将其中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 属性的值 xxxAutoConfiguration 添加到容器中。


@ComponentScan

@ComponentScan 注解的功能就是自动扫描并加载符合条件的组件(eg:@Component,@Controller …),最终将这些 bean 定义加载到 IOC 容器中。


以上自定义的 starter 为例

在这里插入图片描述

还记得我们自定义 starter 过程中有在 META-INF/spring.factories 中指定自动配置类的全限定类名,按照上述的讲解,这个自动配置类会被 springboot 获取到并注入到 IOC 容器,如下图,果然被加入进了需要注入的自动配置类列表中。

在这里插入图片描述
我们再来看看 HelloServiceAutoConfiguration 类中做了什么?

// 声明此类为配置类,和 xml 配置文件注入方式作用相同
@Configuration
// 启动指定类的 ConfigurationProperties 功能,简单来说,将配置文件的值和 HelloProperties 属性进行绑定,我们在Yml中设置的属性都是通过与 xxxProperties 类中的属性进行绑定实现的
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    private final HelloProperties helloProperties;

    public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    @Bean
    @ConditionalOnMissingBean		// HelloService 类不存在时执行此方法,并且使用yml中配置的属性值传入作为构造参数新建 HelloService 实例
    public HelloService helloService() {
        return new HelloService(helloProperties.getName(), helloProperties.getAge(), helloProperties.getHometown());
    }
}

一旦这个配置类生效,并且符合所指定的条件(@ConditionalOnMissingBean)就会向 IOC 容器中注入 HelloService ,HelloService 类的属性是从对应的 properties 类中获取的,而这些属性的值又是和配置文件中绑定的。

所有能在配置文件中能配置的属性都是在 xxxProperties 类中封装着的,配置文件能配置什么就可以参照某个功能对应的xxxProperties 类。


总结

springboot在启动时为我们注入了大量的 xxxAutoConfiguration 类,而这些类是通过扫描类路径下的 META-INF/spring.factories 文件获得的。这些 xxxAutoConfiguration 类生效后,又会向IOC容器中注入 各 xxxAutoConfiguration 对应的业务类,而这些业务类的属性值又被封装在 xxxProperties 类中,而 xxxProperties 类中的属性对应的值又和 在引入此 starter 的工程中yml中定义的属性相绑定。

所以,springboot中完成一个类的自动注入,需要有如下两个类的配合才行。

  • xxxAutoConfiguration:自动配置类,向容器添加组件
  • xxxProperties:封装配置文件中相关属性
评论 194
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值