一、为什么要封装Starter?
在企业级开发中,我们经常需要将通用能力(如鉴权、日志、分布式锁等)抽象为可复用的组件。Spring Boot Starter 的封装能带来三大核心优势:
- 配置统一管理 - 通过
application.properties
实现"开箱即用",避免重复配置 - 依赖自动装配 - 按需加载Bean,解决传统组件依赖复杂的问题
- 版本统一控制 - 在父POM中管理依赖版本,规避兼容性风险
举个实际痛点 :
传统JWT工具类需要每个项目手动配置密钥、过期时间等参数,而通过Starter封装后,只需引入依赖即可直接注入预配置的Bean。
二、手把手实现JWT Starter
1. 创建模块 & 初始化依赖
按Spring官方规范命名模块:jwt-spring-boot-starter
xml
<!-- 核心依赖 -->
<dependencies>
<!-- JJWT 相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- 配置元数据生成 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. 创建配置文件 & Properties类
创建 resources/config/jwt-default.properties
此文件用于在Starter导入其他模块下,其他模块下没有配置jwt属性时,使用Starter内部的配置文件属性。
properties
jwt.key=default_secret_key
jwt.access_token_ttl=300000
jwt.refresh_token_ttl=604800000
创建 JwtProperties
类:
java
// main/java/.../config
@Data
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private String key;
private long accessTokenTtl;
private long refreshTokenTtl;
}
3. 实现自动配置类
java
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
@PropertySource("classpath:/config/jwt-default.properties")
@ConditionalOnClass(Jwts.class) // 当JJWT存在时生效
@ConditionalOnProperty(prefix = "jwt", name = "enabled", matchIfMissing = true)
public class JwtAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户未自定义时生效
public JwtUtil jwtUtil(JwtProperties properties) {
return new JwtUtil(properties);
}
}
4. 编写JWT核心工具类
java
public class JwtUtil {
private final JwtProperties properties;
public JwtUtil(JwtProperties properties) {
this.properties = properties;
}
// 生成AccessToken(示例代码)
public String createAccessToken(ClaimDTO claims) {
return Jwts.builder()
.setClaims(convertToMap(claims))
.setExpiration(new Date(System.currentTimeMillis() + properties.getAccessTokenTtl()))
.signWith(Keys.hmacShaKeyFor(properties.getKey().getBytes()))
.compact();
}
// Token校验(返回枚举更规范)
public TokenStatus validateToken(String token) {
try {
parseToken(token);
return TokenStatus.VALID;
} catch (ExpiredJwtException e) {
return TokenStatus.EXPIRED;
}
// ... 其他异常处理
}
}
5. 注册自动配置
在 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
中写入:
markdown
com.example.jwt.config.JwtAutoConfiguration
注意: 此注册自动配置为Spring Boot 3.x 版本。
三、在项目中集成Starter
1. 引入依赖
xml
<dependency>
<groupId>com.example</groupId>
<artifactId>jwt-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
2. 自定义配置(可选)
在 application.yml
中覆盖默认值:
yaml
jwt:
key: your_secure_key_here
access-token-ttl: 3600000 # 1小时
3. 直接注入使用
java
@RestController
public class AuthController {
private final JwtUtil jwtUtil;
// 构造器注入
public AuthController(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@PostMapping("/login")
public String login(@RequestBody User user) {
// ... 验证逻辑
return jwtUtil.createAccessToken(user);
}
}
4. 注意事项
小nuo发现,对于Starter导入其他模块时,其他模块下的配置文件没有配置Starter所需的配置时,Starter的默认配置丢失,解决方案:
- 在
jwtProperties
类中直接设置默认值
java
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
// 直接设置字段默认值
private String secretKey = "default-secret";
private long expiration = 3600L;
}
-
使用
@PropertySource
从而确保使用Starter内部配置文件 因为Spring Boot配置加载优先级的原因:Spring Boot的配置加载遵循特定优先级,后加载的配置会覆盖先加载的 。
如果其他模块的配置文件中定义了同名属性,会覆盖Starter的默认配置。
若模块未显式配置属性,但Starter的默认值未生效,需检查Starter的默认值设置方式:出现此问题就需要使用到
@PropertySource
注解,就如上示例使用方法一致。
四、配置加载优先级解密
当自定义配置与Starter默认配置冲突时,Spring Boot按以下优先级处理(从高到低):
- 命令行参数
java -jar app.jar --jwt.key=cli_key
- 应用配置文件
application.properties
>application.yml
- Starter默认配置
jwt-default.properties
- 代码默认值
JwtProperties
类中的字段初始值
五、封装经验总结
-
避免过度设计
首版只需实现核心功能,迭代中逐步添加如Redis令牌黑名单等高级特性
-
防御性编程
- 对密钥进行非空校验:
Assert.hasText(properties.getKey(), "JWT密钥不能为空")
- Token解析增加空值判断
- 对密钥进行非空校验:
现在已经可以举一反三学会封装其他的啦!
六、踩坑警示录
典型问题1 :Bean注入冲突
👉 现象 :启动报 No qualifying bean of type 'JwtUtil'
✅ 解决方案 :检查是否误加了 @Component
注解,应通过自动配置类创建Bean
典型问题2 :配置未生效
👉 现象 :修改 application.yml
后仍使用默认值
✅ 排查步骤:
- 检查配置项命名是否符合
kebab-case
(如access-token-ttl
) - 确认配置路径是否被更高优先级的源覆盖
讨论话题:你在封装Starter时遇到过哪些棘手问题?欢迎在评论区分享经验给小nuo!