带你深入了解 SpringBoot 自动配置原理及自定义Starter

1.SpringBoot自动配置原理

从@SpringBootApplication注解开始说,这个注解是一个复合注解,他是由以下几个注解构成的。

// 用于讲其他配置类,注入到spring ioc中的

@SpringBootConfiguration

// 自动配置最重要的注解

@EnableAutoConfiguration

// 用于扫描其他注解(@service、@controller)等等

@ComponentScan(

    excludeFilters = {@Filter(

    type = FilterType.CUSTOM,

    classes = {TypeExcludeFilter.class}

), @Filter(

    type = FilterType.CUSTOM,

    classes = {AutoConfigurationExcludeFilter.class}

)}

)


记得点赞收藏加关注哦 ,需要下载PDF版本和获取更多知识点、面试题的朋友可以加q群:580763979   备注:简书   免费领取~

接下来从@EnableAutoConfiguration开始讲起


其中关键的地方就是这个AutoConfigurationImportSelector类

@Import({AutoConfigurationImportSelector.class})


他里面的selectImports方法中getAutoConfigurationEntry就是获取自动配置的重要组成

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

    return NO_IMPORTS;

} else {

    AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);

    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

}

}

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(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.getConfigurationClassFilter().filter(configurations);

    this.fireAutoConfigurationImportEvents(configurations, exclusions);

    return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);

}

}


通过上述获取在META-INF/spring.factories,

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;

}


在spring.factories中会存在很多这样的键值对

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\


当springboot启动的时候就会加载这些xxxAutoConfigureation,这里以RedisAutoConfiguration为例,介绍是如何进行配置的。

// 只有符合这种要求的,才会将xxxAutoConfigureation加载到spring中

@ConditionalOnClass({RedisOperations.class})

// 开启配置类

@EnableConfigurationProperties({RedisProperties.class})

@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})

public class RedisAutoConfiguration {


RedisProperties配置类,看到这个类,是不是很熟悉,他就是我们在yml里面配置的东西

@ConfigurationProperties(

    prefix = "spring.redis"

)

public class RedisProperties {

    private int database = 0;

    private String url;

    private String host = "localhost";

    private String username;

    private String password;

    private int port = 6379;

    private boolean ssl;

    private Duration timeout;

    private Duration connectTimeout;

    private String clientName;

}


总结:通过上述流程实现将快速配置,减少了繁琐的xml配置,如果要配置,只需简单的在yml配置即可。

2.自定义starter,简单实现一个线程池的创建

创建一个thread-pool-execute-starter的工程

// 添加依赖

<groupId>com.angel.item</groupId>

<artifactId>thread-pool-execute-starter</artifactId>

<version>1.0</version>

<dependencies>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter</artifactId>

    </dependency>

</dependencies>


自动配置类

@Configuration

// 配置配置属性

@EnableConfigurationProperties(ThreadPoolExecutorProperties.class)

// 只有这个类才会生校

@ConditionalOnClass(ThreadPoolExecutor.class)

public class ThreadPoolAutoConfiguration {

    /**

    * 阻塞队列

    */

    private final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(4);

    /**

    * 拒绝策略

    */

    private final RejectedExecutionHandler reject = new ThreadPoolExecutor.AbortPolicy();

    /**

    * 线程池类型:CPU密集型:1;IO密集型:2

    */

    @Value("${scenes}")

    private Integer scenes;

    /**

    * 核心线程数大小

    */

    private Integer corePoolSize;

    /**

    * 最大线程数大小

    */

    private Integer maximumPoolSize;

    /**

    * 空闲线程存活时长

    */

    private Long keepAliveTime;

    /**

    * 存活时长单位

    */

    private TimeUnit unit;

    @PostConstruct

    public void init() {

        // 获取系统CPU核心数

        int cpuCoreNumber = Runtime.getRuntime().availableProcessors();

        this.corePoolSize = cpuCoreNumber;

        this.maximumPoolSize = 25 * cpuCoreNumber;

        this.keepAliveTime = 60 * 3L;

        this.unit = TimeUnit.SECONDS;

    }

    /**

    * N: CPU核心数

    * CPU密集型:corePoolSize = N + 1

    * IO密集型:corePoolSize = 2 * N

    */

    @Bean

    public ThreadPoolExecutor threadPoolExecutor() {

        // cpu密集型

        if (scenes == 1) {

            corePoolSize = corePoolSize + 1;

        } else {

            // io密集型

            corePoolSize = 2 * corePoolSize;

        }

        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, reject);

    }

}


配置类属性

@ConfigurationProperties(

        prefix = "thread.pool"

)

public class ThreadPoolExecutorProperties {

    private Integer scenes = 1;

    public Integer getScenes() {

        return scenes;

    }

    public void setScenes(Integer scenes) {

        this.scenes = scenes;

    }

}


在resources中新建META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

com.angel.item.ThreadPoolAutoConfiguration


将工程重新打包,mvn clean install -U

在另外的工程中引入上面的坐标

<dependency>

    <groupId>com.angel.item</groupId>

    <artifactId>thread-pool-execute-starter</artifactId>

    <version>1.0</version>

</dependency>


在yml配置即可

thread:

  pool:

    scenes: 1


mvn package 重新打包

总结:通过上述配置自定义starter就可以实现了

总结

我这里也准备了一线大厂面试资料和超硬核PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作!

有需要的朋友可以加q群:580763979   备注:简书   免费领取~

推荐阅读更多精彩内容