111 lines
4.9 KiB
Markdown
111 lines
4.9 KiB
Markdown
---
|
||
id: backend.java-spring.awareness
|
||
title: Java / Spring Boot(了解级):读懂项目结构、IoC 与依赖注入、分层、事务注解和与 Go / Python 的对照
|
||
type: lesson
|
||
domain: backend
|
||
stack: [java-spring]
|
||
tags: [Java, Spring Boot, IoC, 依赖注入, 注解, 事务, JPA, MyBatis, 虚拟线程]
|
||
level: basic
|
||
prerequisites: [backend.go.index]
|
||
est_hours: 2
|
||
related: [backend.java-spring.scenario.call-java-service, backend.java-spring.interview]
|
||
week: [14]
|
||
order: 1
|
||
---
|
||
|
||
# Java / Spring Boot(了解级):读懂项目结构、IoC 与依赖注入、分层、事务注解和与 Go / Python 的对照
|
||
|
||
> 定位:**能读懂、能对接、面试能聊概念**,不作为主线。BOSS直聘 2026-10-23 的调研中,有 AI 应用岗位写了“熟悉 Java / Go / Python 其中之一”,或者团队已有 Java 业务系统需要 Agent 去调用。示例只用 JDK,在 JDK 26(JDK 21+ 即可)上实际运行通过;Spring 代码为示意,未编译。以官方文档为准:https://docs.spring.io/spring-boot/ 。
|
||
|
||
## 解释说明
|
||
|
||
### 与你熟悉的技术对照
|
||
|
||
| 概念 | Java / Spring Boot | Go | Python / FastAPI |
|
||
| --- | --- | --- | --- |
|
||
| 数据类 | `record` | struct | `dataclass` / Pydantic 模型 |
|
||
| 路由 | `@RestController` + `@GetMapping` | Gin 路由 | `@app.get` |
|
||
| 依赖注入 | IoC 容器 + 构造器注入 | 手动在 main 中组装 | `Depends` |
|
||
| 参数校验 | `@Valid` + Bean Validation | binding 标签 | Pydantic |
|
||
| 数据访问 | Spring Data JPA / MyBatis | database/sql、sqlc、GORM | SQLAlchemy |
|
||
| 事务 | `@Transactional` | 手动 `tx.Begin/Commit` | `session.begin()` |
|
||
| 并发 | 线程池、虚拟线程(JDK 21) | goroutine | asyncio |
|
||
| 构建 | Maven / Gradle | go build | uv / pip |
|
||
|
||
### Spring 的核心概念
|
||
|
||
- **IoC 容器**:对象(Bean)由容器创建和管理,你声明依赖,容器注入。推荐**构造器注入**(依赖不可变、便于测试)。
|
||
- **分层**:Controller(接收请求)→ Service(业务逻辑、事务)→ Repository(数据访问)。
|
||
- **自动配置**:Spring Boot 根据依赖和配置自动创建常用组件(数据源、Web 服务器),配置在 `application.yml`。
|
||
- **`@Transactional` 的坑**:基于代理实现,同一个类内部方法调用不经过代理,事务不生效;默认只对运行时异常回滚。
|
||
- **AI 相关**:Spring AI、LangChain4j 等框架让 Java 项目也能接入大模型(版本变化快,以官方文档为准)。
|
||
|
||
### 怎么快速读懂一个 Spring Boot 项目
|
||
|
||
1. 看 `pom.xml` / `build.gradle`:依赖了什么。
|
||
2. 找带 `@SpringBootApplication` 的启动类。
|
||
3. 看 `application.yml`:数据库、端口、外部服务地址。
|
||
4. 从 Controller 顺着调用链读到 Service 和 Repository。
|
||
5. 看全局异常处理(`@RestControllerAdvice`)和拦截器 / 过滤器(鉴权)。
|
||
|
||
## 案例
|
||
|
||
你负责的 Python Agent 需要调用公司的 Java 订单服务:读懂它的接口定义(Controller 和 DTO)、错误码、鉴权方式、分页规则,把它封装成 Agent 的工具。
|
||
|
||
## 代码示例
|
||
|
||
只用 JDK 的对照示例 [examples/java-basics/OrderDemo.java](examples/java-basics/OrderDemo.java)(`java OrderDemo.java` 运行通过):record、接口 + 构造器注入、Optional、Stream 分组汇总、BigDecimal 金额、虚拟线程。
|
||
|
||
```text
|
||
跨租户查询被拒绝: 订单不存在: B001
|
||
t1 按状态汇总: {paid=20.00, refunded=5.00}
|
||
用 double 计算金额的问题: 0.1 + 0.2 = 0.30000000000000004
|
||
1000 个虚拟线程计数结果: 1000
|
||
```
|
||
|
||
对应的 Spring Boot 写法(示意,未编译):
|
||
|
||
```java
|
||
@RestController
|
||
@RequestMapping("/orders")
|
||
class OrderController {
|
||
private final OrderService service;
|
||
OrderController(OrderService service) { this.service = service; } // 构造器注入,无需 @Autowired
|
||
|
||
@GetMapping("/{id}")
|
||
OrderDto get(@PathVariable String id, @RequestHeader("X-Tenant-Id") String tenant) {
|
||
return OrderDto.from(service.get(tenant, id));
|
||
}
|
||
}
|
||
|
||
@Service
|
||
class OrderService {
|
||
private final OrderRepository repo;
|
||
OrderService(OrderRepository repo) { this.repo = repo; }
|
||
|
||
@Transactional
|
||
public void refund(String tenant, String id) { /* 修改订单 + 写退款记录,在一个事务中 */ }
|
||
}
|
||
|
||
@RestControllerAdvice
|
||
class GlobalErrors {
|
||
@ExceptionHandler(NotFoundException.class)
|
||
ResponseEntity<Map<String, String>> notFound(NotFoundException e) {
|
||
return ResponseEntity.status(404).body(Map.of("code", "NOT_FOUND", "message", e.getMessage()));
|
||
}
|
||
}
|
||
```
|
||
|
||
## 易错点
|
||
|
||
- 金额用 `double`。
|
||
- 同类内部调用带 `@Transactional` 的方法,以为事务生效。
|
||
- 受检异常被捕获后吞掉,事务没有回滚。
|
||
- 在面试中把“了解”说成“精通”:如实说明能读懂并对接,主语言是 Go / Python。
|
||
|
||
## 面试常问
|
||
|
||
- 什么是 IoC 和依赖注入?
|
||
- `@Transactional` 什么情况下会失效?
|
||
- 你不会 Java,如何和 Java 团队协作?
|