问题 :在 Spring 框架中对于Bean要怎么设计以及使用呢 举个贴合生产的例子
用一个电商下单的真实场景,把三层架构完整走一遍。

先看整体结构:现在用完整代码走一遍"用户下单"这个流程:
Repository 层 --- 只管数据库操作
java
// 只负责一件事:操作数据库
// 没有任何业务逻辑,没有任何可变状态
@Repository
public class OrderRepository {
private final JdbcTemplate jdbc; // Spring 提供的数据库工具,也是 Bean
public OrderRepository(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
// 保存订单到数据库
public void save(Order order) {
jdbc.update(
"INSERT INTO orders(user_id, product_id, amount) VALUES (?,?,?)",
order.getUserId(), order.getProductId(), order.getAmount()
);
}
// 查询用户的所有订单
public List<Order> findByUserId(Long userId) {
return jdbc.query(
"SELECT * FROM orders WHERE user_id = ?",
(rs, row) -> new Order(rs.getLong("id"), rs.getLong("user_id")),
userId
);
}
}
@Repository
public class UserRepository {
private final JdbcTemplate jdbc;
public UserRepository(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
public User findById(Long userId) {
return jdbc.queryForObject(
"SELECT * FROM users WHERE id = ?",
(rs, row) -> new User(rs.getLong("id"), rs.getString("name")),
userId
);
}
// 扣减用户余额
public void deductBalance(Long userId, BigDecimal amount) {
jdbc.update(
"UPDATE users SET balance = balance - ? WHERE id = ?",
amount, userId
);
}
}
Service 层 --- 只管业务逻辑
java
// 核心:业务规则在这里,调用多个 Repository 协作
// 没有可变状态,所有字段都是 final
@Service
public class OrderService {
// 注入需要的 Repository,都是单例,共用同一个内存地址
private final OrderRepository orderRepository;
private final UserRepository userRepository;
public OrderService(OrderRepository orderRepository,
UserRepository userRepository) {
this.orderRepository = orderRepository;
this.userRepository = userRepository;
}
// 业务方法:下单
// 注意:@Transactional 保证这三步要么全成功,要么全回滚
@Transactional
public Order placeOrder(Long userId, Long productId, BigDecimal amount) {
// 业务规则1:检查用户是否存在
User user = userRepository.findById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 业务规则2:检查余额是否足够
if (user.getBalance().compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
// 业务规则3:扣减余额
userRepository.deductBalance(userId, amount);
// 业务规则4:创建订单记录
Order order = new Order(userId, productId, amount);
orderRepository.save(order);
return order;
// 三步全成功 → 事务提交
// 任何一步抛异常 → 事务回滚,数据库恢复原样
}
}
Controller 层 --- 只管接收请求和返回响应
less
// 只负责:接收 HTTP 请求、调 Service、把结果包装成 JSON 返回
// 不写任何业务逻辑,不写任何 SQL
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService; // 注入 Service
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
// 接收 POST /orders 请求
@PostMapping
public ResponseEntity<String> placeOrder(@RequestBody PlaceOrderRequest req) {
// 参数校验
if (req.getUserId() == null || req.getAmount() == null) {
return ResponseEntity.badRequest().body("参数不能为空");
}
// 调 Service 处理业务
try {
Order order = orderService.placeOrder(
req.getUserId(),
req.getProductId(),
req.getAmount()
);
return ResponseEntity.ok("下单成功,订单ID:" + order.getId());
} catch (RuntimeException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
请求完整流程
scss
POST /orders
↓
OrderController.placeOrder() ← 接收请求,校验参数
↓
OrderService.placeOrder() ← 执行业务规则,控制事务
↓
UserRepository.findById() ← 查用户
UserRepository.deductBalance() ← 扣余额
OrderRepository.save() ← 存订单
↓
返回 "下单成功,订单ID:123"
Bean 设计的三条规则
规则一:每层只做自己的事,不越界
typescript
// ❌ 错误:Controller 里写 SQL
@PostMapping
public String placeOrder() {
jdbc.update("INSERT INTO orders..."); // Controller 不该碰数据库
}
// ❌ 错误:Repository 里写业务规则
public void save(Order order) {
if (order.getAmount() < 0) { ... } // 业务规则不该放这里
}
规则二:Bean 不存可变状态
java
// ❌ 危险:Service 里存了用户信息(多线程共享会出错)
@Service
public class OrderService {
private User currentUser; // 不同请求的线程会互相覆盖这个值
}
// ✅ 正确:需要的数据通过方法参数传递
public Order placeOrder(Long userId, ...) {
User user = userRepository.findById(userId); // 每次从数据库取
}
规则三:依赖向下,不向上
kotlin
// ✅ 正确的方向
Controller → Service → Repository → 数据库
// ❌ 错误:Repository 不能依赖 Service
@Repository
public class OrderRepository {
@Autowired
private OrderService orderService; // 禁止!形成循环依赖,Spring 会报错
}
这三条规则保证了:每个 Bean 职责清晰、无状态、可以安全地被单例共享,不管多少个线程同时进来,都不会互相干扰。
生产中一个 Spring 应用启动后,这四个 Bean 在单例池里各自只有一个实例,从应用启动到关闭,全程就这一份。
但是你可能会有一个疑问:
如果只有一个 OrderService,同一时刻有 1000 个用户同时下单,1000 个请求同时进来,都用同一个 OrderService 对象,不会乱吗?
答案是不会,原因是这样的:
java
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final UserRepository userRepository;
// 1000 个请求同时进来,调的是同一个方法
@Transactional
public Order placeOrder(Long userId, BigDecimal amount) {
// 但是每个请求的 userId、amount 是各自的局部变量
// 局部变量存在各自线程的栈内存里,互不干扰
User user = userRepository.findById(userId);
userRepository.deductBalance(userId, amount);
Order order = new Order(userId, amount); // 每个请求 new 自己的 Order
orderRepository.save(order);
return order;
}
}
关键在于:userId、amount、user、order 这些都是方法内的局部变量 ,每个线程有自己独立的栈,局部变量互不共享。OrderService 对象本身只有两个 final 的依赖字段,从不改变。
用一张图说清楚:
ini
单例池(共享) 线程栈(每个请求独立)
───────────────── ──────────────────────────
OrderService @7a4f ← 线程A:userId=1, amount=100, order=OrderA
OrderService @7a4f ← 线程B:userId=2, amount=200, order=OrderB
OrderService @7a4f ← 线程C:userId=3, amount=300, order=OrderC
同一个对象,但每个线程的局部变量完全独立
1000 个请求共用同一个 OrderService,但每个请求的数据活在自己的线程栈里,谁也看不到谁的数据。
所以生产中的真实情况是:
| 对象 | 数量 | 存在哪里 |
|---|---|---|
OrderController |
1 个 | 单例池 |
OrderService |
1 个 | 单例池 |
OrderRepository |
1 个 | 单例池 |
UserRepository |
1 个 | 单例池 |
每次请求的 Order 对象 |
每个请求一个 | 线程栈/堆,用完 GC 回收 |
| 每次请求的参数(userId 等) | 每个请求一份 | 线程栈,方法结束即消失 |
Bean 是单例复用的,但每次请求产生的数据是独立的、临时的。这就是为什么 Bean 设计成无状态之后,单例是完全安全的。