前言

在使用 Spring Boot 开发项目时,我们常常依赖各种 Starter,它们为我们提供了开箱即用的功能,例如自动配置、依赖管理和更便捷的集成支持。但在某些场景下,我们可能会遇到业务需求或技术需要,现有的 Starter 无法完全满足。这时候,自定义一个 Spring Boot Starter 就显得尤为重要。

通过自定义 Starter,我们可以将通用逻辑、组件和配置进行模块化封装,提升代码复用性和项目一致性。无论是为团队打造共享工具库,还是为开源社区提供扩展包,自定义 Starter 都是极具价值的开发技能。本文将带你了解自定义 Starter 的核心原理,并通过一个简单的本地缓存示例,帮助你快速掌握如何实现一个属于自己的 Spring Boot Starter。

本地缓存项目

在这个DEMO中,我们将基于Caffeine构建一个自定义starter项目,以实现本地缓存功能。然后其它项目可以通过引入starter的方式,使用本地缓存的增删查功能。

创建local-cache项目

首先我们创建一个local-cache项目,整个项目的结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local-cache/
├── src/
├── main/
├── java/
├── com/
├── btzx/
├── localcache/
├── CaffeineAutoConfiguration.java
├── CaffeineCacheManager.java
└── CaffeineProperties.java
├── resources/
├── META-INF/
├── spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── application.yml
└── pom.xml

pom.xml添加依赖

接着,在pom.xml中引入Caffeine相关的依赖,pom.xml中要注意的是artifactId的命名,我们按照官方的建议明明为lc-spring-boot-starter

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>

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

添加自定义属性配置类

新建一个CaffeineProperties属性配置类,我们这次只简单定义两个的属性,即容量和过期时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.btzx.localcache;

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

@ConfigurationProperties(prefix = "lc.caffeine")
public class CaffeineProperties {

private int capacity = 1024;

private int expireAfterWrite = 10;

public int getCapacity() {
return capacity;
}

public void setCapacity(int capacity) {
this.capacity = capacity;
}

public int getExpireAfterWrite() {
return expireAfterWrite;
}

public void setExpireAfterWrite(int expireAfterWrite) {
this.expireAfterWrite = expireAfterWrite;
}
}

CaffeineCacheManager

CaffeineCacheManager类来声明对缓存的操作,我们能只声明三个简单的操作,即增查删。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.btzx.localcache;

import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.stereotype.Component;

@Component
public class CaffeineCacheManager {

private final Cache<String, Object> cache;

public CaffeineCacheManager(Cache<String, Object> cache) {
this.cache = cache;
}

public void put(String key, Object value) {
cache.put(key, value);
}

public Object get(String key) {
return cache.getIfPresent(key);
}

public void delete(String key) {
cache.invalidate(key);
}
}

CaffeineAutoConfiguration配置类

这个类主要使用自定义属性来完成Caffeine缓存的构建,以及将CaffeineCacheManager声明为Bean等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.btzx.localcache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import java.util.concurrent.TimeUnit;

@AutoConfiguration
@EnableConfigurationProperties(CaffeineProperties.class)
public class CaffeineAutoConfiguration {


private final CaffeineProperties caffeineProperties;

public CaffeineAutoConfiguration(CaffeineProperties caffeineProperties) {
this.caffeineProperties = caffeineProperties;
}

@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.expireAfterWrite(caffeineProperties.getExpireAfterWrite(), TimeUnit.SECONDS)
.maximumSize(caffeineProperties.getCapacity())
.build();
}

@Bean
public CaffeineCacheManager caffeineCacheManager(Cache<String, Object> caffeineCache) {
return new CaffeineCacheManager(caffeineCache);
}
}

声明自动装配配置

到此,我们使用JAVA来完成的配置功能就已经全部完成了,我们还需要完成最后一项配置,让Spring Boot自动装配我们指定的Bean。

在resources配置文件夹下新建META-INF文件夹,接着在META-INF文件夹下新建spring文件夹,最后在spring文件夹下新建配置文件:org.springframework.boot.autoconfigure.AutoConfiguration.imports,并在里面添加属性:

1
com.btzx.localcache.CaffeineAutoConfiguration

这样就完成了自动装配的配置。

打包并使用starter

在local-cache项目更目录下使用

1
mvn clean install

命令来完成打包,install会同时将打好的包推送到本地仓库中,这样我们就可以在其它项目中引入这个jar包并直接使用它提供的缓存能力了。

这里不再展示完成项目,只展示关键部分代码。

首先是在pom.xml中添加自定义starter的依赖:

1
2
3
4
5
<dependency>
<groupId>com.btzx</groupId>
<artifactId>btzx-spring-boot-starter-local-cache</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

接着就可以直接在默认配置下使用本地缓存功能了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.btzx.lcboot;

import com.btzx.localcache.CaffeineCacheManager;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/cache")
public class CacheController {

@Resource
private CaffeineCacheManager caffeineCacheManager;

@GetMapping("/setValue")
public String putValue(@RequestParam String key, @RequestParam String value) {
caffeineCacheManager.put(key, value);
return "ok, " + key + "=" + value;
}
@GetMapping("/query")
public String queryValue(@RequestParam String key) {
Object object = caffeineCacheManager.get(key);
return object == null ? "" : object.toString();
}
@GetMapping("/delete")
public String delete(@RequestParam String key) {
caffeineCacheManager.delete(key);
return "Key deleted successfully!";
}
}

starter原理

Spring Boot Starter 的核心在于 自动装配机制,主要依靠以下几个关键点:

  1. 配置文件:SpringBoot2.7+之前的是spring.factories,之后的版本可以用org.springframework.boot.autoconfigure.AutoConfiguration.imports,文件中指定需要加载的自动配置类,从而在应用启动时完成相关 Bean 的注册。
  2. 条件注解:自动配置类通常会使用条件注解(例如 @ConditionalOnClass@ConditionalOnProperty)来决定是否加载某些配置或组件,以此保证 Starter 的灵活性。
  3. 依赖管理:Starter 通常是依赖的集合,它通过 pom.xml 文件统一管理模块的必需依赖,避免了开发者手动引入和配置的繁琐工作。

总结

好了,文章写到这里,你是不是已经从“Starter 是个啥?”的小白升级成“我自己能整一个 Starter!”的大佬了?咱们一路从 Spring Boot 的自动配置原理聊到怎么用 org.springframework.boot.autoconfigure.AutoConfiguration.imports 把自动配置安排得明明白白,再到条件注解的骚操作,全流程都解锁了!

自定义 Starter 就像是给项目配个“万能开关”,自己写过的通用功能打包一发,团队用起来轻松,自己看着也舒坦。只要掌握以下几个套路,分分钟就能实现:

  1. org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:告诉 Spring Boot 该去哪加载自动配置类,配置一下,启动时全搞定!
  2. 自动配置类:写好核心逻辑,用各种条件注解(比如 @ConditionalOnClass)让它只在合适的时候加载。
  3. 别乱塞东西:Starter 不是百宝箱,保持精简和专注很重要!

当然,搞 Starter 也别太随意。比如条件注解一定要用对,配置灵活性不能少;另外,文档写好点,免得两个月后自己用都一头雾水。

最后,别忘了,自定义 Starter 的核心目的就是让自己和队友少搬砖,多享受写代码的快乐。如果你觉得这篇文章帮到你了,那就赶紧去试试动手造一个吧,代码的快乐是无限的!