SpringBoot自定义Starter

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

一、先搞懂:Starter核心原理

SpringBoot Starter的本质是自动配置+依赖封装,目的是简化第三方组件/自定义功能的集成成本,无需手动编写大量配置,只需引入Starter即可开箱即用。

核心机制:

  • 自动配置类:标记@Configuration,定义Bean创建规则
  • 条件注解:控制Bean的创建时机(如依赖存在、配置开启时才加载)
  • SPI机制:SpringBoot启动时扫描指定文件,加载自动配置类
  • 依赖传递:Starter内部封装所需依赖,引入方无需重复导包

SpringBoot 2.7+ 废弃了传统的spring.factories,改用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件注册自动配置类,本文以新版规范为主,同时兼容低版本写法。

Starter自动装配全生命周期链路

针对本次hello-spring-boot-starter,自动装配并非随机生效,而是遵循SpringBoot固定的SPI+容器托管流程,全程分为启动扫描→配置绑定→Bean注册→业务注入四大核心阶段,每一步都有严格的触发规则:

阶段1:项目启动,SPI机制扫描自动配置类

  1. SpringBoot项目启动时,执行自动配置加载器,扫描类路径下的指定SPI文件
  2. 定位到Starter中resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
  3. 读取文件内的全限定类名,加载HelloAutoConfiguration自动配置类,将其纳入Spring容器的配置管理池

阶段2:开启配置绑定,映射yml参数

  1. 自动配置类上的@EnableConfigurationProperties(HelloProperties.class)触发生效,开启配置绑定功能
  2. 扫描HelloProperties类上的@ConfigurationProperties(prefix = "hello")注解
  3. 读取项目application.yml中hello.开头的配置项,通过setter方法赋值给HelloProperties的对应字段
  4. 生成携带配置参数的HelloProperties单例Bean,存入Spring容器

阶段3:条件校验,注册核心业务Bean

  1. 校验自动配置类上的@ConditionalOnClass(HelloService.class):类路径存在HelloService则继续,否则跳过装配
  2. 执行@Bean修饰的方法,Spring容器按类型自动注入已存在的HelloProperties Bean(无需@Autowired)
  3. 校验@ConditionalOnMissingBean:容器无自定义HelloService Bean时,执行new HelloService(helloProperties)创建实例
  4. 将HelloService实例注册为Spring容器Bean,交由容器管理生命周期

阶段4:业务调用,容器注入Bean使用

  1. 测试项目中通过@Resource/@Autowired注解声明HelloService属性
  2. Spring容器根据类型匹配,将已注册的HelloService Bean注入到Controller中
  3. 调用sayHello()方法,底层读取HelloProperties的配置参数,返回拼接后的结果

链路核心总结:SPI文件定位配置类 → 注解激活配置绑定 → 条件判断创建Bean → 容器托管注入使用,全程无需手动扫描、无需手动new对象,实现真正的开箱即用。

二、前期准备

1. 环境要求

  • JDK 8+
  • Maven 3.6+
  • SpringBoot 2.7.x / 3.x(本文以2.7.15为例)

2. 项目命名规范

官方Starter命名:spring-boot-starter-xxx;自定义Starter推荐:xxx-spring-boot-starter(避免与官方冲突)。

示例:本次创建hello-spring-boot-starter,实现自定义问候功能的自动装配。

三、分步实现:自定义Starter

Step1:创建Maven空项目(无启动类)

Starter是普通Maven项目,无需SpringBoot启动类,仅作为依赖包存在。创建完成后,配置pom.xml核心依赖:

<?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>

    <!-- 自定义Starter坐标 -->
    <groupId>com.example</groupId>
    <artifactId>hello-spring-boot-starter</artifactId>
    <version>1.0.0</version>

    <!-- 父工程:统一SpringBoot版本 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.15</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!-- 自动配置核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!-- 配置处理器:生成配置元数据,IDE提示配置项 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <!-- 打包配置:跳过测试,避免打包失败 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step2:编写配置属性类

用于绑定application.yml中的自定义配置,实现参数可配置。

package com.example.hello.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 自定义配置绑定类
 * prefix:配置文件中的前缀
 */
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
    /**
     * 问候前缀,默认值:Hello
     */
    private String prefix = "Hello";
    /**
     * 问候后缀,默认值:!
     */
    private String suffix = "!";

    // getter & setter
    public String getPrefix() {
        return prefix;
    }
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    public String getSuffix() {
        return suffix;
    }
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

Step3:编写业务功能类

定义Starter提供的核心业务逻辑,后续由自动配置类加载为Bean。

package com.example.hello.service;

import com.example.hello.config.HelloProperties;

public class HelloService {
    private final HelloProperties helloProperties;

    // 构造注入配置属性
    public HelloService(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    /**
     * 自定义问候方法
     * @param name 用户名
     * @return 拼接后的问候语
     */
    public String sayHello(String name) {
        return helloProperties.getPrefix() + " " + name + helloProperties.getSuffix();
    }
}

Step4:编写自动配置类

核心类,控制HelloService的创建条件,实现自动装配。

package com.example.hello.config;

import com.example.hello.service.HelloService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自动配置类
 */
@Configuration
// 当HelloService类存在时,才加载该配置
@ConditionalOnClass(HelloService.class)
// 开启配置绑定(使HelloProperties生效)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {

    /**
     * 注册HelloService Bean
     * ConditionalOnMissingBean:容器中无该Bean时才创建,避免覆盖用户自定义Bean
     */
    @Bean
    @ConditionalOnMissingBean
    public HelloService helloService(HelloProperties helloProperties) {
        return new HelloService(helloProperties);
    }
}

常用条件注解小结: - @ConditionalOnClass:类路径存在指定类时生效 - @ConditionalOnMissingBean:容器无指定Bean时生效 - @ConditionalOnProperty:配置文件存在指定属性时生效 - @ConditionalOnWebApplication:Web环境下生效

Step5:注册自动配置类(SPI核心)

SpringBoot启动时会扫描该文件,加载自动配置类。

  1. 在resources目录下创建目录:META-INF/spring
  2. 新建文件:org.springframework.boot.autoconfigure.AutoConfiguration.imports
  3. 文件内容:写入自动配置类的全限定类名
com.example.hello.config.HelloAutoConfiguration

低版本兼容(SpringBoot 2.7以下):创建META-INF/spring.factories,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.hello.config.HelloAutoConfiguration

Step6:打包并安装到本地仓库

执行Maven命令,将Starter打包为jar并安装到本地Maven仓库,供其他项目引入:

# 清理、打包、安装
mvn clean install

安装成功后,可在本地Maven仓库找到com.example/hello-spring-boot-starter/1.0.0目录。

四、测试:引入自定义Starter

Step1:新建SpringBoot Web项目

在新项目的pom.xml中引入自定义Starter依赖:

<!-- 引入自定义Starter -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>hello-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

Step2:配置自定义参数(可选)

application.yml中修改默认配置,不配置则使用默认值:

hello:
  prefix: 你好
  suffix: ~

Step3:编写测试接口

package com.example.test.controller;

import com.example.hello.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class HelloController {

    // 直接注入Starter提供的Bean
    @Resource
    private HelloService helloService;

    @GetMapping("/hello/{name}")
    public String hello(@PathVariable String name) {
        return helloService.sayHello(name);
    }
}

Step4:启动测试

启动SpringBoot项目,访问接口:http://localhost:8080/hello/张三,返回结果:你好 张三~,说明Starter生效。

五、进阶优化技巧

  • 配置元数据:依赖spring-boot-configuration-processor后,打包会自动生成spring-configuration-metadata.json,IDE会提示配置项
  • 开关控制:添加@ConditionalOnProperty(prefix = "hello", name = "enable", havingValue = "true"),实现按需开启Starter
  • 依赖隔离:将非核心依赖设为<optional>true</optional>,避免传递不必要的依赖
  • 多环境适配:结合@Profile注解,实现不同环境的差异化配置

六、常见避坑点

  1. 自动配置类的包路径与启动类包路径不一致时,无需手动扫描,SPI机制会自动加载
  2. 切勿在Starter中添加@SpringBootApplication启动类,否则会导致冲突
  3. 配置绑定类必须添加get/set方法,否则无法注入配置值
  4. SpringBoot 3.x需使用Jakarta注解,替换javax包

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

Spring 文章被收录于专栏

本专栏聚焦Spring全生态体系,从IoC/AOP核心原理入手,覆盖Spring Boot自动配置、事务管理、Web开发等实战内容。拆解循环依赖、动态代理等高频面试难点,助力开发者从入门到精通,打通单体到微服务的技术链路,解决企业级开发痛点,提升架构设计与问题排查能力,成为Java后端进阶的必备技术专栏。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务