SpringBoot那些过时写法
SpringBoot一路2.x升级到了写这个文章时的4.x。从刚接触SpringBoot框架的时候2.x,到宣布不再支持2.x和jdk8,最低支持jdk17,当时还给了我一点小小的震撼,不是说好了你发任你发,我用Java8吗? 但是2.x整个不再支持的情况下还是乖乖升级吧。Spring 团队在加速推进现代化,该废弃的废弃,该移除的移除。下面就简单记录下一些曾经的标准写法到现在的不推荐或者直接移除的用法吧。
代码层
@Autowired 字段注入 → 构造器注入
@Controller
public class UserController {
@Autowired
private UserService userSerive;// 无法加 final,测试难搞
}
这是体验感影响最大的一个,当时学习Spring的时候所有教程都是所有@Autowired当时觉得真的方便极了,面试还有控制反转和依赖注入的八股文😂 现在变成了不推荐的用法,IDEA中现在也会报红提示不再推荐使用。Spring 官方现在只认可两种注入方式:构造器注入和 Setter 注入。字段注入已经不作为正式的 DI 选项了。
@Service
public class UserService {
private final UserMapper userMapper;
// Spring 4.3 起,单一构造器无需 @Autowired
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
-
过时原因:字段注入导致 NPE 风险(无法实例化)、对象不可变(
final)被破坏、隐藏类间依赖、单元测试必须启动容器。 -
推荐构造器注入(配合 Lombok
@RequiredArgsConstructor更简洁)。
@RestController
@RequiredArgsConstructor // 生成构造器
public class UserController {
private final UserService userService; // 不可变,安全
}
@Value散落配置→ @ConfigurationProperties
一直觉得@Value这个注解不好用,自从知道了@ConfigurationProperties注解后我是再也不喜欢用它了,特别是微服务项目有人在代码中写N个这玩意看到就火大😤 再加上nacos中的N个配置文件,人都找麻了。
@Service
public class MailService {
@Value("${mail.host}")
private String host;
@Value("${mail.port}")
private int port; // 若配置写错,启动不报错,运行时抛异常
}
-
过时原因:配置散落各处难维护,拼写错误只能运行时发现,无类型安全(如端口转 int 需手动转)。
-
替代方案:
- 使用
@ConfigurationProperties绑定配置前缀,支持校验和 IDE 补全。
java
@ConfigurationProperties(prefix = "app")@Componentpublic class AppProperties {@NotBlankprivate String name;@Min(1) @Max(65535)private int port;@NotNullprivate int timeout;// getters / setters} - 使用
@Transactional 常见误用
-
过时/错误原因:只读操作未优化数据库连接;
private方法失效;吞掉Exception导致不回滚(默认只回滚RuntimeException)。 -
旧写法(有坑):
@Servicepublic class OrderService {@Transactional // 没指定 rollbackForpublic void createOrder() throws Exception {// 如果抛出 Exception(非Runtime),不会回滚!throw new Exception("error");}@Transactional // 只读查询不加 readOnly,浪费连接public List<Order> getList() { ... }} -
新写法(最佳实践):
java
@Servicepublic class OrderService {// 增删改:明确指定 rollbackFor@Transactional(rollbackFor = Exception.class)public void createOrder() throws Exception { ... }// 查询:明确标注 readOnly,性能优化@Transactional(readOnly = true)public List<Order> getList() { ... }}
System.out.println → SLF4J + @Slf4j
经典问题,到底是什么神人会在生产代码里留下这玩意啊!
-
过时原因:无日志级别控制、无法按环境开关、同步阻塞 I/O、脱离 ELK 等日志收集体系。
-
旧写法:
public class PaymentService {public void pay() {System.out.println("支付金额:" + amount); // 生产环境无法关闭}} -
新写法:
import lombok.extern.slf4j.Slf4j;@Slf4jpublic class PaymentService {public void pay(BigDecimal amount) {log.info("支付金额:{}", amount); // 占位符,避免字 符串拼接}}
@Resource / @Inject 包路径迁移
-
过时原因:Jakarta EE 9+ 替换 Java EE,
javax包名改为jakarta,否则注入完全失效。 -
旧写法:
import javax.annotation.Resource;
@Resource
private CacheManager cacheManager;
-
新写法:
import jakarta.annotation.Resource; // 包名改变@Resourceprivate CacheManager cacheManager;
Spring Assert 工具类方法重载移除
这个好多人都没用过,其实就是官方小工具,我让AI总结了一下效果如下:
Spring Assert 是 Spring Framework 中位于
org.springframework.util包下的一个轻量级断言工具类,主要用于在业务代码中进行防御性参数校验。它通过简洁的静态方法替代繁琐的if-throw代码块,当条件不满足时抛出运行时异常(通常是IllegalArgumentException或IllegalStateException),从而提升代码的可读性和整洁度。与 JUnit 的断言不同,Spring Assert 始终生效,无需 JVM 参数开启,专为生产环境的参数校验设计,是线上代码的第一道防线。
核心常用方法
- 非空与内容检查
notNull(Object object, String message):检查对象是否为null,否则抛出异常。hasText(String text, String message):检查字符串是否不为null且包含非空白字符(即 trim 后长度大于 0)。hasLength(String text, String message):检查字符串是否不为null且长度大于 0(允许纯空格)。notEmpty(Collection/Map/Array, String message):检查集合、映射或数组是否不为null且至少包含一个元素。- 布尔条件与状态检查
isTrue(boolean expression, String message):检查表达式是否为true,常用于复杂逻辑校验。state(boolean expression, String message):功能类似isTrue,但抛出IllegalStateException,语义上更适用于对象状态不符合预期的场景。- 类型与元素检查
isInstanceOf(Class clazz, Object obj, String message):检查对象是否为指定类的实例。noNullElements(Collection/Array, String message):遍历集合或数组,确保其中不包含null元素。代码对比示例
使用 Assert 可以显著简化传统的参数校验逻辑:
java// 传统写法public void register(String username, Integer age) {if (username == null || username.trim().isEmpty()) {throw new IllegalArgumentException("用户名不能为空");}if (age == null || age < 0) {throw new IllegalArgumentException("年龄必须大于等于 0");}// 业务逻辑...}// 使用 Spring Assert 重构public void register(String username, Integer age) {Assert.hasText(username, "用户名不能为空");Assert.isTrue(age != null && age >= 0, "年龄必须大于等于 0");// 业务逻辑...}高级特性与最佳实践
- 延迟消息构建:Spring 5.0+ 版本支持传入
Supplier<String>作为消息参数。仅在断言失败时才生成消息字符串,避免在正常路径下产生不必要的性能开销。- 统一异常处理:Assert 抛出的
IllegalArgumentException可配合全局异常处理器(如@RestControllerAdvice),统一转换为标准的 HTTP 400 错误响应,便于前端处理。- 适用场景:最适合在公共方法入口处进行快速失败(Fail-fast)校验;对于复杂的跨字段校验或数据库校验,仍需结合自定义逻辑,但可用
isTrue作为最终断言。
-
过时原因:Spring 6.0 移除了不带
message参数的断言方法,必须显式提供异常信息。 -
旧写法:
import org.springframework.util.Assert;Assert.notNull(obj); // 编译报错 -
新写法:
Assert.notNull(obj, "对象不能为空");