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

springboot-starter介绍

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

spring-starter的官方命名规范:

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

自定义命名空间

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

自定义starter demo

项目结构:

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chuan</groupId>
    <artifactId>dataTest-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.3.RELEASE</version>
        <relativePath/>
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <!-- 引入spring-boot-starter,所有starter的基本配合 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

    </dependencies>

</project>

然后定义一个实体类,@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,并将写好的配置类的路径放在里面。为什么这么做后面解释。

最后打包 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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chuan</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

        <!-- 引入自定义starter -->
        <dependency>
            <groupId>com.chuan</groupId>
            <artifactId>dataTest-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

编写配置文件application.properties

data.service.dataString=2020

编写一个测试类

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

运行测试:

自动装配源码分析

测试类启动入口: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查看方法,debug运行,有的是进不去这个方法的(要看springboot版本),将断点打到下一步就能拦截到。 (这里需要再分析一下执行过程)

   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());
        }
    }

4.继续查看getAutoConfigurationEntry方法

 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //继续向里面看
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            //下面是一层层的判重验证等处理
            Set<String> 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<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> 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<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

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

暂时先简单跟一下源码。

猜你喜欢

转载自blog.csdn.net/qq_39404258/article/details/108346523