前言
在使用 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项目更目录下使用
命令来完成打包,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 的核心在于 自动装配机制,主要依靠以下几个关键点:
- 配置文件:SpringBoot2.7+之前的是spring.factories,之后的版本可以用org.springframework.boot.autoconfigure.AutoConfiguration.imports,文件中指定需要加载的自动配置类,从而在应用启动时完成相关 Bean 的注册。
- 条件注解:自动配置类通常会使用条件注解(例如
@ConditionalOnClass
、@ConditionalOnProperty
)来决定是否加载某些配置或组件,以此保证 Starter 的灵活性。
- 依赖管理:Starter 通常是依赖的集合,它通过
pom.xml
文件统一管理模块的必需依赖,避免了开发者手动引入和配置的繁琐工作。
总结
好了,文章写到这里,你是不是已经从“Starter 是个啥?”的小白升级成“我自己能整一个 Starter!”的大佬了?咱们一路从 Spring Boot 的自动配置原理聊到怎么用 org.springframework.boot.autoconfigure.AutoConfiguration.imports
把自动配置安排得明明白白,再到条件注解的骚操作,全流程都解锁了!
自定义 Starter 就像是给项目配个“万能开关”,自己写过的通用功能打包一发,团队用起来轻松,自己看着也舒坦。只要掌握以下几个套路,分分钟就能实现:
- org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:告诉 Spring Boot 该去哪加载自动配置类,配置一下,启动时全搞定!
- 自动配置类:写好核心逻辑,用各种条件注解(比如
@ConditionalOnClass
)让它只在合适的时候加载。
- 别乱塞东西:Starter 不是百宝箱,保持精简和专注很重要!
当然,搞 Starter 也别太随意。比如条件注解一定要用对,配置灵活性不能少;另外,文档写好点,免得两个月后自己用都一头雾水。
最后,别忘了,自定义 Starter 的核心目的就是让自己和队友少搬砖,多享受写代码的快乐。如果你觉得这篇文章帮到你了,那就赶紧去试试动手造一个吧,代码的快乐是无限的!