|
|
|
|
@ -0,0 +1,407 @@
|
|
|
|
|
# 代码优化建议报告 (JDK 25 + Spring Boot 3.5.7)
|
|
|
|
|
|
|
|
|
|
## 📋 概述
|
|
|
|
|
|
|
|
|
|
本报告基于 Context7 最新文档和代码审查,针对 JDK 25 + Spring Boot 3.5.7 环境,提供代码质量、可读性和潜在 Bug 的优化建议。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔴 严重问题(需要立即修复)
|
|
|
|
|
|
|
|
|
|
### 1. 异常处理不当 - `printStackTrace()`
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-support/src/main/java/com/java3y/austin/support/config/OkHttpConfiguration.java:85`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 使用 `e.printStackTrace()` 输出到控制台,不利于生产环境日志管理
|
|
|
|
|
- 方法返回 `null`,可能导致 NPE
|
|
|
|
|
|
|
|
|
|
**修复建议**:
|
|
|
|
|
```java
|
|
|
|
|
@Bean
|
|
|
|
|
public SSLSocketFactory sslSocketFactory() {
|
|
|
|
|
try {
|
|
|
|
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
|
|
|
|
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
|
|
|
|
|
return sslContext.getSocketFactory();
|
|
|
|
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
|
|
|
|
log.error("Failed to create SSL socket factory", e);
|
|
|
|
|
throw new IllegalStateException("Failed to initialize SSL context", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**影响**:
|
|
|
|
|
- ⚠️ 生产环境无法正确记录错误
|
|
|
|
|
- ⚠️ 可能导致 Bean 创建失败,应用启动异常
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 2. 异常处理工具类混用
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-web/src/main/java/com/java3y/austin/web/exception/ExceptionHandlerAdvice.java:5`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 使用了 `org.assertj.core.util.Throwables`(测试框架的工具类)
|
|
|
|
|
- 应该使用 `com.google.common.base.Throwables`(项目其他地方统一使用)
|
|
|
|
|
|
|
|
|
|
**修复建议**:
|
|
|
|
|
```java
|
|
|
|
|
import com.google.common.base.Throwables; // 替换 org.assertj.core.util.Throwables
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**影响**:
|
|
|
|
|
- ⚠️ 引入了测试依赖到生产代码
|
|
|
|
|
- ⚠️ 可能导致依赖冲突
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 3. JDK 版本检测逻辑过时
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-support/src/main/java/com/java3y/austin/support/utils/ConcurrentHashMapUtils.java:17`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 使用 `System.getProperty("java.version").startsWith("1.8.")` 检测 Java 8
|
|
|
|
|
- JDK 25 环境下,版本格式为 `25` 或 `25.0.x`,此逻辑失效
|
|
|
|
|
|
|
|
|
|
**修复建议**:
|
|
|
|
|
```java
|
|
|
|
|
static {
|
|
|
|
|
try {
|
|
|
|
|
String version = System.getProperty("java.version");
|
|
|
|
|
// JDK 9+ 格式: "9", "10", "11", "25" 等
|
|
|
|
|
// JDK 8 格式: "1.8.0_xxx"
|
|
|
|
|
IS_JAVA8 = version.startsWith("1.8.") || version.startsWith("1.7.");
|
|
|
|
|
} catch (Exception ignore) {
|
|
|
|
|
IS_JAVA8 = false; // 默认假设不是 Java 8
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**或者直接移除该检查**(因为项目已升级到 JDK 25):
|
|
|
|
|
```java
|
|
|
|
|
public static <K, V> V computeIfAbsent(ConcurrentMap<K, V> map, K key, Function<? super K, ? extends V> func) {
|
|
|
|
|
// JDK 9+ 已修复 computeIfAbsent 性能问题,直接使用
|
|
|
|
|
return map.computeIfAbsent(key, func);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**影响**:
|
|
|
|
|
- ⚠️ 可能导致性能优化逻辑错误执行
|
|
|
|
|
- ⚠️ 不必要的代码复杂性
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🟡 中等优先级问题(建议修复)
|
|
|
|
|
|
|
|
|
|
### 4. Null 安全检查不足
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-handler/src/main/java/com/java3y/austin/handler/action/SendMessageAction.java:27`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- `taskInfo.getSendChannel()` 可能返回 null
|
|
|
|
|
- `taskInfo.getReceiver()` 可能返回 null 或空集合
|
|
|
|
|
|
|
|
|
|
**修复建议**:
|
|
|
|
|
```java
|
|
|
|
|
@Override
|
|
|
|
|
public void process(ProcessContext<TaskInfo> context) {
|
|
|
|
|
TaskInfo taskInfo = context.getProcessModel();
|
|
|
|
|
Integer sendChannel = taskInfo.getSendChannel();
|
|
|
|
|
|
|
|
|
|
if (sendChannel == null) {
|
|
|
|
|
log.warn("Send channel is null, taskInfo: {}", taskInfo);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Set<String> receivers = taskInfo.getReceiver();
|
|
|
|
|
if (CollUtil.isEmpty(receivers)) {
|
|
|
|
|
log.warn("Receivers is empty, taskInfo: {}", taskInfo);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ... 后续逻辑
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5. 资源关闭可能异常
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-handler/src/main/java/com/java3y/austin/handler/receiver/redis/RedisReceiver.java:112`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- `InterruptedException` 被捕获后,在 catch 块中又捕获了新的异常,可能导致异常信息丢失
|
|
|
|
|
|
|
|
|
|
**修复建议**:
|
|
|
|
|
```java
|
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
|
log.error("RedisReceiver#receiveMessage interrupted", ex);
|
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 6. 类型转换未进行空值检查
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/send/SendAssembleAction.java:88`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- `messageTemplate.get()` 可能返回 null(虽然已检查 isPresent,但后续使用仍需注意)
|
|
|
|
|
|
|
|
|
|
**当前代码**:
|
|
|
|
|
```java
|
|
|
|
|
Optional<MessageTemplate> messageTemplate = messageTemplateDao.findById(messageTemplateId);
|
|
|
|
|
if (!messageTemplate.isPresent() || messageTemplate.get().getIsDeleted().equals(CommonConstant.TRUE)) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
List<TaskInfo> taskInfos = assembleTaskInfo(sendTaskModel, messageTemplate.get());
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**建议**: 使用 `orElseThrow()` 更清晰:
|
|
|
|
|
```java
|
|
|
|
|
MessageTemplate template = messageTemplateDao.findById(messageTemplateId)
|
|
|
|
|
.orElseThrow(() -> new CommonException(RespStatusEnum.TEMPLATE_NOT_FOUND));
|
|
|
|
|
|
|
|
|
|
if (template.getIsDeleted().equals(CommonConstant.TRUE)) {
|
|
|
|
|
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.TEMPLATE_NOT_FOUND));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
List<TaskInfo> taskInfos = assembleTaskInfo(sendTaskModel, template);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🟢 代码质量和可读性优化
|
|
|
|
|
|
|
|
|
|
### 7. 使用构造函数注入替代字段注入
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 项目中大量使用 `@Autowired` 字段注入(151 处)
|
|
|
|
|
- 不符合 Spring Boot 最佳实践(推荐构造函数注入)
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 优先使用构造函数注入,提高可测试性和依赖清晰度
|
|
|
|
|
- 对于可选依赖,可以使用 `@Autowired(required = false)` 或 `Optional<>`
|
|
|
|
|
|
|
|
|
|
**示例**:
|
|
|
|
|
```java
|
|
|
|
|
// 当前方式
|
|
|
|
|
@Service
|
|
|
|
|
public class SendMessageAction {
|
|
|
|
|
@Autowired
|
|
|
|
|
private HandlerHolder handlerHolder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 推荐方式
|
|
|
|
|
@Service
|
|
|
|
|
public class SendMessageAction {
|
|
|
|
|
private final HandlerHolder handlerHolder;
|
|
|
|
|
|
|
|
|
|
public SendMessageAction(HandlerHolder handlerHolder) {
|
|
|
|
|
this.handlerHolder = handlerHolder;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 8. 使用 Lombok 简化代码
|
|
|
|
|
|
|
|
|
|
**已优化文件**: `ExceptionHandlerAdvice.java` 使用了 `@Slf4j`,但其他类仍使用 `LoggerFactory`
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 统一使用 `@Slf4j` 注解
|
|
|
|
|
- 使用 `@RequiredArgsConstructor` 替代手动构造函数注入
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 9. 字符串拼接优化
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-web/src/main/java/com/java3y/austin/web/exception/ExceptionHandlerAdvice.java:31`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 使用 `"\r\n" + errStackStr + "\r\n"` 字符串拼接
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- JDK 25 可以使用字符串模板(需启用预览特性)
|
|
|
|
|
- 或使用 `String.format()` 或 `MessageFormat`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 10. 使用 `@PreDestroy` 和 `@PostConstruct` 的改进
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-handler/src/main/java/com/java3y/austin/handler/receipt/MessageReceipt.java`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
- 使用 `javax.annotation.*`(JSR-250),在 JDK 11+ 中需要单独引入依赖
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 在 JDK 25 环境下,可以继续使用(Spring Boot 已包含)
|
|
|
|
|
- 或考虑使用 Spring 的生命周期回调接口
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 11. Switch 表达式可以进一步优化
|
|
|
|
|
|
|
|
|
|
**已优化文件**: `SensWordsAction.java` 已使用 switch 表达式
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 检查其他文件是否还有传统 switch-case,可统一优化
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 12. Optional 使用可以更优雅
|
|
|
|
|
|
|
|
|
|
**已优化文件**: `Receiver.java` 已使用 `Optional.ifPresent()`
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 其他文件中的 Optional 使用可以继续优化
|
|
|
|
|
- 避免 `isPresent() + get()` 的组合,使用 `orElse()`, `orElseThrow()` 等
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔵 Spring Boot 3.5.7 特性优化
|
|
|
|
|
|
|
|
|
|
### 13. 虚拟线程配置检查
|
|
|
|
|
|
|
|
|
|
**已配置**: `application.properties` 中已启用虚拟线程
|
|
|
|
|
|
|
|
|
|
**建议验证**:
|
|
|
|
|
```properties
|
|
|
|
|
# 确认虚拟线程已正确启用
|
|
|
|
|
spring.threads.virtual.enabled=true
|
|
|
|
|
spring.threads.virtual.scheduler.name-prefix=austin-virtual-
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**验证方法**:
|
|
|
|
|
- 检查线程名称是否包含 `austin-virtual-` 前缀
|
|
|
|
|
- 使用 `ProcessInfo.VirtualThreadsInfo` 监控虚拟线程状态
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 14. 优雅关闭配置
|
|
|
|
|
|
|
|
|
|
**已配置**: `server.shutdown=graceful`
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 确保所有线程池都注册到 `ThreadPoolExecutorShutdownDefinition`(已实现)
|
|
|
|
|
- 验证关闭超时时间是否合理(当前 20 秒)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 15. 配置属性验证
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 使用 `@ConfigurationProperties` + `@Valid` 进行配置验证
|
|
|
|
|
- 对于必需配置,使用 `@NotNull` 或 `@NotEmpty`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📊 性能优化建议
|
|
|
|
|
|
|
|
|
|
### 16. 集合初始化容量优化
|
|
|
|
|
|
|
|
|
|
**已优化**: 部分文件已使用 `new HashSet<>(size)` 指定初始容量
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 继续检查其他集合初始化,确保容量设置合理
|
|
|
|
|
- 避免集合频繁扩容
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 17. Stream API 优化
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 对于大数据量处理,考虑使用 `parallelStream()`(需注意线程安全)
|
|
|
|
|
- 避免在 Stream 中进行复杂的数据库查询
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 18. Redis 操作优化
|
|
|
|
|
|
|
|
|
|
**已优化**: `SimpleLimitService` 使用 pipeline 批量操作
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 继续检查其他 Redis 操作,优先使用批量操作
|
|
|
|
|
- 考虑使用 Redis 事务或 Lua 脚本减少网络往返
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🐛 潜在 Bug
|
|
|
|
|
|
|
|
|
|
### 19. 整数溢出风险
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java:69`
|
|
|
|
|
|
|
|
|
|
**问题**:
|
|
|
|
|
```java
|
|
|
|
|
keyValues.put(key, String.valueOf(Integer.parseInt(inRedisValue.get(key)) + 1));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**风险**:
|
|
|
|
|
- 如果计数超过 `Integer.MAX_VALUE`,会发生溢出
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 使用 `Long` 类型
|
|
|
|
|
- 或添加溢出检查
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 20. 字符编码处理
|
|
|
|
|
|
|
|
|
|
**文件**: `austin-support/src/main/java/com/java3y/austin/support/utils/AustinFileUtils.java`
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 确保所有文件读取操作明确指定 UTF-8 编码
|
|
|
|
|
- 避免使用系统默认编码
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 21. 日期时间处理
|
|
|
|
|
|
|
|
|
|
**建议**:
|
|
|
|
|
- 检查是否所有日期时间操作都使用 `java.time.*` API(JDK 8+)
|
|
|
|
|
- 避免使用 `java.util.Date` 和 `Calendar`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📝 总结
|
|
|
|
|
|
|
|
|
|
### 立即修复(高优先级)
|
|
|
|
|
1. ✅ `OkHttpConfiguration.java` - 异常处理和日志
|
|
|
|
|
2. ✅ `ExceptionHandlerAdvice.java` - 依赖替换
|
|
|
|
|
3. ✅ `ConcurrentHashMapUtils.java` - JDK 版本检测逻辑
|
|
|
|
|
|
|
|
|
|
### 建议修复(中优先级)
|
|
|
|
|
4. ⚠️ Null 安全检查增强
|
|
|
|
|
5. ⚠️ 资源关闭异常处理
|
|
|
|
|
6. ⚠️ Optional 使用优化
|
|
|
|
|
|
|
|
|
|
### 代码质量提升(低优先级)
|
|
|
|
|
7. 💡 构造函数注入
|
|
|
|
|
8. 💡 Lombok 优化
|
|
|
|
|
9. 💡 Switch 表达式统一
|
|
|
|
|
10. 💡 Spring Boot 3.5.7 特性充分利用
|
|
|
|
|
|
|
|
|
|
### 性能优化
|
|
|
|
|
11. ⚡ 集合初始化容量
|
|
|
|
|
12. ⚡ Stream API 优化
|
|
|
|
|
13. ⚡ Redis 批量操作
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🚀 下一步行动
|
|
|
|
|
|
|
|
|
|
1. **立即修复**严重问题(1-3)
|
|
|
|
|
2. **逐步优化**中等优先级问题(4-6)
|
|
|
|
|
3. **持续改进**代码质量(7-10)
|
|
|
|
|
4. **性能测试**验证优化效果(11-13)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**生成时间**: 2024年
|
|
|
|
|
**基于版本**: JDK 25 + Spring Boot 3.5.7
|
|
|
|
|
**检查工具**: Context7 + 代码审查
|