SpringBoot2.x系列教程60--SpringBoot如何自定义Starter启动器?

1,122 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第34天,点击查看活动详情

前言

壹哥之前给大家说过,SpringBoot中的各种依赖包,很多时候都是基于starter启动器来实现的。我们之前一直使用SpringBoot中或者第三方公司开发的各种starter启动器,那么我们自己能不能定义启动器呢?只要你有需求,壹哥 就能帮你搞定哦。所以今天我们来学习starter启动器的原理,已经如何实现自定义的启动器。

一. SpringBoot中的Starter

1. Starter的概念

SpringBoot之所以大大地简化了我们的开发,用到的一个很重要的技术就是Starter机制!

Starter机制抛弃了以前xml中繁杂的配置,将各种配置统一集成进了Starter中,开发人员只需要在maven中引入Starter依赖,SpringBoot就能自动扫描出要加载的配置信息并按相应的默认配置来启动项目。

所以Starter可以理解为一个可拔插式的插件,提供了一系列便利的依赖描述符,使得我们可以获得所需的所有Spring和相关技术的一站式服务。应用程序只需要在maven中引入Starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置,我们可以把Starter看做是Springboot的场景启动器。

2. Starter的优点

  • Starter可以让我们摆脱开发过程中对各种依赖库的冲突处理;
  • 可以简化原有xml中各种负载的配置信息;
  • SpringBoot提供了针对一般研发中各种场景的spring-boot-starter依赖,所有这些依赖模块都遵循着约定成俗的默认配置(”约定大于配置“),并允许我们调整这些配置。Starter的出现极大的帮助开发者们从繁琐的框架配置中解放出来,从而更专注于业务代码。

3. 常用的Starter

4.Starter的组成

在springboot官方文档中,特别提到,一个完整的Spring Boot Starter可能包含以下组件:

  • autoconfigure模块: 包含自动配置的代码;
  • starter模块: 提供对autoconfigure模块的依赖,以及一些其它的依赖。

但是如果我们不需要区分这两个概念的话,也可以把自动配置的代码模块与依赖管理模块合并成一个模块,官方文档中也有说明:

You may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concerns。

如果不需要将自动配置代码和依赖项管理分离开来,则可以将它们组合到一个模块中。

5. SpringBoot的AutoConfiguration机制

如果要标记一个应用为Springboot应用,需要添加一个SpringBootApplication注解,下面是一个标准的spring boot启动程序。

其中SpringBootApplication注解上面又有一个EnableAutoConfiguration注解,EnableAutoConfiguration注解就是自动加载配置的关键。

@EnableAutoConfiguration是一个组合注解,利用Import把AutoConfigurationImportSelector注解导入到容器中。

Springboot启动的时候会加载所有的selector并执行selectImports方法,这个方法会加载spring-boot-autoconfigure依赖中的/META-INF/spring.factories配置文件,利用该文件中的EnableAutoConfiguration,从而实现自动配置。

当Spring Boot启动时,它会在类路径中查找名为spring.factories的文件,我们可以在其中发现一些比较眼熟的单词,比如Aop,Rabbit,Cache...当Springboot启动的时候,将会尝试加载这些配置类,如果该路径下存在该类的话,则将运行它,并初始化与该配置类相关的bean。

二. 自定义Starter启动器

1. 自定义Starter的场景

在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,只需要将其在pom中引用依赖即可,利用SpringBoot为我们完成自动装配即可。

常见的自定义Starter场景比如:

  • 动态数据源
  • 登录模块
  • 基于AOP技术实现日志切面
  • ...

2. 自定义Starter命名规范

Springboot官方建议其官方推出的starter以spring-boot-starter-xxx的格式来命名,而第三方开发者自定义的starter则以xxxx-spring-boot-starter的规则来命名,比如 mybatis-spring-boot-starter。

3. 几个重要注解

  • @Configuration: 表明此类是一个配置类,将变为一个bean被Spring进行管理;
  • @EnableConfigurationProperties: 启用属性配置,将读取指定类里面的属性;
  • @ConditionalOnClass: 当类路径下面有指定的类时,进行自动配置;
  • @ConditionalOnProperty:判断指定的属性是否具备指定的值;
  • @ConditionalOnMissingBean:当容器中没有指定bean是,创建此bean;
  • @Import: 引入其他的配置类。

4. 创建一个starter项目

4.1 添加依赖

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

4.2 定义一个Properties类映射配置信息

package com.yyg.boot.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: 一一哥
 * @Blame: yyg
 * @Since: Created in 2021/3/9
 */
@Data
@ConfigurationProperties(prefix = "myweb.config")
public class MyWebProperties {

    private Integer port;

    private String name;

    private String info;

}

4.3 定义一个Service

package com.yyg.boot.service;

/**
 * @Author: 一一哥
 * @Blame: yyg
 * @Since: Created in 2021/3/9
 */
public class ConfigService {

    private Integer port;

    private String name;

    private String info;

    public ConfigService(Integer port, String name, String info) {
        this.port = port;
        this.name = name;
        this.info = info;
    }

    public String showConfig() {
        return this.port + "--" + this.name + "--" + this.info;
    }

}

4.4 定义一个配置类

package com.yyg.boot.config;

import com.yyg.boot.properties.MyWebProperties;
import com.yyg.boot.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: 一一哥
 * @Blame: yyg
 * @Since: Created in 2021/3/9
 * @Description:
 */
@SuppressWarnings("all")
@Configuration
@EnableConfigurationProperties(value = MyWebProperties.class)
@ConditionalOnProperty(prefix = "myweb.config", name = "enable", havingValue = "true")
public class MyWebConfig {

    @Autowired
    private MyWebProperties properties;

    @Bean
    public ConfigService defaultWebConfig() {
        return new ConfigService(properties.getPort(), properties.getName(), properties.getInfo());
    }

}

4.5 创建spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yyg.boot.config.MyWebConfig

4.6 项目包结构

5. 创建一个测试项目

5.1 添加依赖

 <dependencies>
    <dependency>
       <groupId>com.yyg.boot</groupId>
        <artifactId>hello-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

5.2 创建Controller

package com.yyg.boot.web;

import com.yyg.boot.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 一一哥
 * @Blame: yyg
 * @Since: Created in 2021/3/9
 * @Description: ${cursor}$
 */
@RestController
public class HelloController {

    @Autowired
    private ConfigService configService;

    @GetMapping("/getConfig")
    private String getConfigInfo() {

        return configService.showConfig();
    }

}

5.3 创建配置文件

myweb:
  config:
    enable: true
    port: 8080
    name: 'myWebApp'
    info: 'custom web info...'

5.4 创建入口类

package com.yyg.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Author: 一一哥
 * @Blame: yyg
 * @Since: Created in 2021/3/9
 * @Description: ${cursor}$
 */
@SpringBootApplication
public class TestStarterApplication {

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

}

5.5 项目包结构

6. 启动项目测试

可以看到我们的自定义starter,已经实现!你学会了吗?评论区告诉壹哥吧。