带你深入了解 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   备注:简书   免费领取~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容