springboot自定义starter及自动配置源码分析

springboot-starter介绍

SpringBoot 最强大的功能之一就是把我们常用的场景抽取成了一个个starter(场景启动器),可插拔的特性。

spring-starter的官方命名规范:

  • 前缀:spring-boot-starter-
  • 模式:spring-boot-starter-模块名

自定义命名空间

  • 后缀:-spring-boot-starter
  • 模式:模块-spring-boot-starter

自定义starter demo

项目结构:

springboot自定义starter及自动配置源码分析_第1张图片

首先创建一个空的maven项目,添加maven依赖:



    4.0.0

    com.chuan
    dataTest-spring-boot-starter
    1.0-SNAPSHOT
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.3.RELEASE
        
    
    
        UTF-8
        UTF-8
        1.8
    
    

        
        
            org.springframework.boot
            spring-boot-starter
        

    

然后定义一个实体类,@ConfigurationProperties为获取配置里的值,在没写配置类之前这个会报错,暂时忽略

@ConfigurationProperties("data.service")
public class DataTest {
    private String dataString;

    public String getDataString() {
        return dataString;
    }

    public void setDataString(String dataString) {
        this.dataString = dataString;
    }
}

一个平淡无奇的服务类

package service;

public class DataService {
    private DataTest dataTest;

    public DataTest getDate() {
        return dataTest;
    }

    public void setDate(DataTest date) {
        this.dataTest = date;
    }
    public String show(){
        return  dataTest.getDataString();
    }
}

最后是配置类,

@EnableConfigurationProperties注解应用到你的@Configuration时, 任何被@ConfigurationProperties注解的beans将自动被Environment属性配置。 这种风格的配置特别适合与SpringApplication的外部YAML配置进行配合使用。

@Configuration
@EnableConfigurationProperties(DataTest.class)
public class DataConfig {
    @Autowired
    private DataTest dataTest;
    @Bean
    public DataService dataService(){
        DataService dataService = new DataService();
        dataService.setDate(dataTest);
        return dataService;
    }

}

最重要的一点:创建一个META-INF/spring.factories,并将写好的配置类的路径放在里面。为什么这么做后面解释。

springboot自定义starter及自动配置源码分析_第2张图片

最后打包 package

mvn install:install-file -DgroupId=com.chuan -DartifactId=dataTest-spring-boot-starter -Dversion=1.0.0 -Dpackaging=jar -Dfile=C:\Users\chuan\Desktop\glzxCode\hello\target\dataTest-spring-boot-starter-1.0-SNAPSHOT.jar

starter测试

再创建一个空的maven项目,修改maven依赖:加入了自定义的starter



    4.0.0

    com.chuan
    test
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.3.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.chuan
            dataTest-spring-boot-starter
            1.0.0
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

编写配置文件application.properties

data.service.dataString=2020

编写一个测试类

@RestController
@RequestMapping("/aaa")
public class HelloController {
    @Autowired
    private DataService dataService;
    @GetMapping("/hello")
    public String hello(){
        return dataService.show();
    }
}

运行测试:

springboot自定义starter及自动配置源码分析_第3张图片

自动装配源码分析

测试类启动入口:1.查看@SpringBootApplication注解

@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class,args);
    }
}

里面有三个注解:

@SpringBootConfiguration ==@Configuration
@EnableAutoConfiguration  自动配置
@ComponentScan  扫描 

2.进入@EnableAutoConfiguration注解

里面又有两个注解:

@AutoConfigurationPackage    添加该注解的类所在的package 作为 自动配置package 进行管理
@Import({AutoConfigurationImportSelector.class})  导入一个AutoConfigurationImportSelector实例

3.进入AutoConfigurationImportSelector.class查看方法,里面有selectImports,按道理讲所有实现了ImportSelector接口的实现类都会在启动时执行selectImports方法,而AutoConfigurationImportSelector也实现了该接口的子接口DeferredImportSelector,但通过断点并没有执行该方法,于是乎深入了解一下流程。

具体源码跟踪,为什么没有执行这个https://www.jianshu.com/p/2a04f7028ee6

   public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

3.5通过断点并没有发现AutoConfigurationImportSelector#selectImports方法被执行,但可以知道调用了第四步的方法。具体实现在

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法里

process源码:


        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
      // 在这里直接调用了第四步的方法
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }

 4.继续查看getAutoConfigurationEntry方法

 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //继续向里面看
            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            //下面是一层层的判重验证等处理
            Set exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

5.继续查看getCandidateConfigurations方法

 protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

6.继续查看loadFactoryNames方法

 public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

7.继续查看loadSpringFactories方法,在这里就能看到之前装配的内容为什么要放在这个目录。

springboot自定义starter及自动配置源码分析_第4张图片

暂时先简单跟一下源码。

你可能感兴趣的:(springboot,spring,boot,starter)