SpringBoot自动配置源码详解及自定义starter代码编写

文章目录

    • 一、源码跟踪
    • 二、自定义spring-boot-starter自动配置
    • 三、总结

一、源码跟踪

1.首先我们知道SpringBoot的核心注解@SpringBootApplication包含@SpringBootConfiguration
、@EnableAutoConfiguration、@ComponentScan
其中@EnableAutoConfiguration是实现自动配置的核心。
点进@EnableAutoConfiguration,代码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

可以看到核心注解为
@Import({AutoConfigurationImportSelector.class})

2.AutoConfigurationImportSelector.class
点进{AutoConfigurationImportSelector.class,可以看到SpringBoot自动配置源码详解及自定义starter代码编写_第1张图片
接下来主要围绕着loadMetadata和getAutoConfigurationEntry展开

3.loadMetadata和getAutoConfigurationEntry
(1)进入loadMetadata,可以看到主要是读取配置文件META-INF/spring-autoconfigure-metadata.properties,把里面的数据放到AutoConfigurationMetadata对象中,这个对象怎么用的呢,我们接着往下看。
SpringBoot自动配置源码详解及自定义starter代码编写_第2张图片
(2)搜索AutoConfigurationMetadata对象,首先在getAutoConfigurationEntry方法中调用,最终在filter方法中使用,filter里面传入的两个参数,另一个参数来自getAutoConfigurationEntry的getCandidateConfigurations
SpringBoot自动配置源码详解及自定义starter代码编写_第3张图片
(3)getCandidateConfigurations
点进getCandidateConfigurations,可以看到最终也是返回一个配置,这个配置来自哪个文件我们从提示大概能猜到是META-INF/spring.factories
在这里插入图片描述
(4)loadFactoryNames
点进loadFactoryNames,发现果然是META-INF/spring.factories
SpringBoot自动配置源码详解及自定义starter代码编写_第4张图片
(5)filter
了解了所有参数之后,我们可以开始看filter了,从名字就能猜到这个方法的作用–过滤器,它过滤了什么呢,接着往下看,可以看到,用AutoConfigurationMetadata和configuration匹配,匹配不上AutoConfigurationMetadata的数据会被过滤掉,具体匹配逻辑现在还不知道,后面会解释
SpringBoot自动配置源码详解及自定义starter代码编写_第5张图片
(6)autoconfigure-metadata.properties和spring.factories
①现在我们知道filter的参数主要来自两个文件autoconfigure-metadata.properties和spring.factories
,在autoconfigure的jar包下找到了这俩文件
SpringBoot自动配置源码详解及自定义starter代码编写_第6张图片
②打开看看,这是autoconfigure-metadata.properties,可以看到里面有些常见的注解,这些是SpringBoot特有的条件注解
SpringBoot自动配置源码详解及自定义starter代码编写_第7张图片
③然后我们再看看spring.factories,存储了很多键值对,org.springframework.boot.autoconfigure.EnableAutoConfiguration这里存了很多逗号隔开的值
SpringBoot自动配置源码详解及自定义starter代码编写_第8张图片
(7)filter匹配
①关于匹配逻辑这一块,一开始我没看懂,于是打了个断点,挑了几个例子
SpringBoot自动配置源码详解及自定义starter代码编写_第9张图片
SpringBoot自动配置源码详解及自定义starter代码编写_第10张图片
SpringApplicationAdminJmxAutoConfiguration、AopAutoConfiguration、BatchAutoConfiguration匹配结果分别是true、true、false
②比如spring.factories中的org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
在这里插入图片描述
在autoconfigure-metadata.properties中搜索,结果如下:
SpringBoot自动配置源码详解及自定义starter代码编写_第11张图片
SpringApplicationAdminJmxAutoConfiguration有两个匹配项,一个带AutoConfigureAfter且后面跟了值,一个不带;
AopAutoConfiguration在autoconfigure-metadata.properties搜不到;
BatchAutoConfiguration有两个匹配项,一个带ConditionalOnBean且后面跟了值,一个不带;
最终只有BatchAutoConfiguration被过滤了
!也就是说匹配的上的也只有部分会被过滤掉。
然后我又跟了下代码,进入OnClassCondition.class有这么一个方法,里面对ConditionalOnBean的类进行了过滤,当ConditionalOnBean后面跟的值,比如BatchAutoConfiguration.ConditionalOnBean = A
,当A不存在时,最终BatchAutoConfiguration不会被自动装配,会被过滤掉。
SpringBoot自动配置源码详解及自定义starter代码编写_第12张图片

二、自定义spring-boot-starter自动配置

最早没打算做这个的,因为发现部分源码还没理解的很清楚,所以决定自己写一个试试。
1.创建一个自定义starter启动器
new一个maven项目,根据starter的命名规范最好是xxx-spring-boot-starter,我这个叫demo-spring-boot-starter
2.项目目录结构如下
SpringBoot自动配置源码详解及自定义starter代码编写_第13张图片
3.pom文件配置
加入如下配置,可以使用@Configuration等注解

 <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-autoconfigureartifactId>
            <version>2.1.5.RELEASEversion>
        dependency>
    dependencies>

4.spring.factories
按照项目目录创建文件,加入下面代码

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.starter.configuration.AutoTestConfiguration

5.AutoTestProperties.java和AutoTestConfiguration.java

package com.starter.configuration;

import com.starter.dao.HealthCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//@Configuration相当于xml中的标签,注入容器
@Configuration
@EnableConfigurationProperties(value = AutoTestProperties.class)
public class AutoTestConfiguration {

    @Autowired
    private AutoTestProperties autoTestProperties;


    //@Bean相当于xml中的标签,注册并管理bean的生命周期
    @Bean
    public HealthCode autowired(){
        HealthCode healthCode = new HealthCode();
        healthCode.setHealthStatus(autoTestProperties.getHealthStatus());
        healthCode.setIdCard(autoTestProperties.getIdCard());
        healthCode.setUsername(autoTestProperties.getUsername());
        System.out.println("自动配置成功");
        return healthCode;
    }
}
package com.starter.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


//prefix在application.yml中使用,用来区分变量值
@ConfigurationProperties(prefix = "healthcode")
public class AutoTestProperties {
    private String username;
    private String idCard;
    private String healthStatus;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public String getHealthStatus() {
        return healthStatus;
    }

    public void setHealthStatus(String healthStatus) {
        this.healthStatus = healthStatus;
    }
}

6.HealthCode.java

package com.starter.dao;

public class HealthCode {
    private String username;
    private String idCard;
    private String healthStatus;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public String getHealthStatus() {
        return healthStatus;
    }

    public void setHealthStatus(String healthStatus) {
        this.healthStatus = healthStatus;
    }
}

7.将sarter项目打包,install即可
SpringBoot自动配置源码详解及自定义starter代码编写_第14张图片
8.将starter引入已有的web项目中,在pom文件中加入下面代码,并更新一下maven

 
        <dependency>
            
            <groupId>org.examplegroupId>
            <artifactId>demo-spring-boot-starterartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>

9.在application.yml文件中加入下面配置,注意前面不要有空格

# 自定义starter配置
healthcode.username: caocao
healthcode.idCard: 362302198001013366
healthcode.healthStatus: 1

10.新建一个controller作为入口测试

package com.example.springb_web.controller;

import com.starter.dao.HealthCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

    @Autowired
    private HealthCode healthCode;

    @RequestMapping(value="/autoTest")
    @ResponseBody
    public HealthCode autoTest(){
        return healthCode;
    }

}

11.编译启动项目,测试
测试网址:http://localhost:8080/autoTest
12.结果如下
在这里插入图片描述

三、总结

SpringBoot的自动配置使用@EnableAutoConfiguration注释实现,是通过找到spring.factories配置文件中的所有XXXAutoConfiguration的自动配置类,并将autoconfigure-metadata.properties中含@ConditionalOnClass@AutoConfigureAfter的配置类过滤掉,然后通过@bean注册,@Configuration加载到容器,实现这些类的自动配置。

你可能感兴趣的:(面试,新手入门教程,spring,boot,java,微服务)