常见问题 FAQ
PHP/Laravel 开发者转向 Spring Boot 的高频疑问汇总。按「启动、依赖注入、数据库、并发、部署」分类。
启动与配置
Q:@SpringBootApplication 放在哪个包?为什么报「找不到组件」?
放所有业务包的最外层。它默认扫描「所在包及其子包」,放错位置会导致 @Service、@RestController 全部扫不到,启动后注入就报错。见 目录结构。
Q:Failed to configure a DataSource 是什么?
你引用了需要数据源的 starter(如 JPA/MyBatis),但没配 spring.datasource.* 或没加驱动。要么配连接,要么去掉不需要的依赖。
Q:端口被占用怎么办?
java -jar demo.jar --server.port=8081
# 或 yml: server.port: 8081Q:Spring Boot 2.x 和 3.x 代码有什么差别?
大部分代码一样。主要差异:包名 javax.* → jakarta.*;Java 版本要求 17+;部分 API 迁移。教程以 3.x 为主,关键处都注明了差异。
依赖注入
Q:@Autowired 报错「required a bean of type 'xxx'」?
三种可能:① 类没加 @Component/@Service 等注解;② 入口类没扫到它(见上面第一个问题);③ 接口有多个实现没指定 @Qualifier。
Q:为什么说构造器注入比 @Autowired 字段注入好?
字段注入无法 final、可能为 null、测试要起容器。构造器注入依赖不可变、创建即完整、测试直接 new。教程示例统一用构造器注入。
Q:@Transactional / @Async 没生效?
同类内部调用不生效(this.method() 不走代理)。必须通过注入的 Bean 调用;只对 public 方法生效;@Async 还需要 @EnableAsync。
数据库
Q:JPA 的 findById 查不到返回什么?
返回 Optional,用 .orElseThrow(...) 或 .orElse(null)。它比 Eloquent 的「查不到返回 null」多一步,但更安全。
Q:LazyInitializationException 是什么?
实体懒加载关联(getPosts()),在事务外访问抛的异常。解决:Service 事务内查全,或 @EntityGraph / JOIN FETCH 预加载。见 JPA 关联关系。
Q:列表查询很慢,N+1 是什么?
查 N 条数据又各查一次关联,共 N+1 次 SQL。用 @EntityGraph 一条 JOIN 解决,等同于 Eloquent 的 with()。见 JPA 查询方法。
Q:ddl-auto 可以设 update 吗?
生产绝对不行。update 让 ORM 自动改表,可能误改生产数据。生产用 none + Flyway 管理表结构。见 JPA 入门。
并发与状态
Q:为什么 Java 容易出「共享变量被改乱」的 bug?
Bean 默认单例,所有请求线程共享。Laravel 的 PHP-FPM 每请求销毁所以不易踩中;Java 长驻进程必须写无状态 Bean。见 Bean 作用域与生命周期。
Q:多实例部署时 Session 登录态丢了?
默认 Session 存内存,每台机器各存各的。用 Redis 存 Session(spring.session.store-type: redis),或改无状态 JWT。见 Session 与 Cookie。
Q:异步任务里拿不到当前登录用户?
SecurityContextHolder 是 ThreadLocal,新线程不会自动带过去。把用户 ID 作为参数传给异步方法。见 Spring Security 深入。
部署
Q:java -jar demo.jar 启动的配置从哪来?
优先级:命令行参数 > 环境变量 > application-{profile}.yml > application.yml > 默认值。生产密钥走环境变量,别写死在 yml。见 配置中心。
Q:打成的 jar 很大(几十 MB)正常吗?
正常。这是 fat jar,内含 Tomcat 和所有依赖,所以能独立运行。体积大是「单文件部署」的代价,换来无需外置服务器。
Q:需要 Nginx 吗?
内置 Tomcat 可直接对外,但生产常在前面加 Nginx(SSL 终结、静态资源、反向代理)。见 构建与部署。
开发习惯
Q:PHP 的空数组 [],Java 里对应什么?
new ArrayList<>() 或 List.of()。注意 Java 的 List 是「不可变/可变」区分的,List.of() 不可增删。
Q:Java 有 isset() / empty() 吗?
没有内置函数,用 Objects.isNull(x)、StringUtils.hasText(s)、Optional。见 常用工具与帮助函数。
Q:怎么写 foreach?
for (Post p : posts) { ... } // foreach
for (int i = 0; i < n; i++) { ... } // 普通 for
posts.forEach(p -> { ... }); // 流式Q:字符串拼接用 . 还是 +?
Java 用 +。"id=" + id 自动转字符串。日志用 {} 占位符(见 日志)。
Q:没有全局函数,很多工具从哪来?
Java 用静态方法(Math.max、StringUtils.hasText)和 Stream API。没有全局函数是 Java 的设计 —— 一切都从属于类。见 常用工具与帮助函数。