别再死记硬背UML类图了!用Java/Spring Boot实战案例,5分钟搞懂依赖、关联、聚合与组合
用Spring Boot实战拆解UML类图:依赖、关联、聚合与组合的代码表达艺术
在Java开发的世界里,UML类图常被视为"设计师的玩具"而遭开发者冷遇。但当你面对一个复杂的Spring Boot微服务系统时,能否准确识别两个类之间是聚合还是组合关系,往往决定了代码的健壮性。本文将以电商系统中的用户-订单-商品模块为例,带你用Spring Boot的视角重新理解这些UML关系。
1. 从Spring Boot看UML四大关系本质
1.1 依赖关系:最松散的临时合作
依赖(Dependency)是UML中最短暂的关系,体现在方法参数、局部变量或静态方法调用中。在Spring Boot中,这种关系常出现在服务层的业务逻辑中:
@Service public class OrderService { // 方法参数形式的依赖 public void validateOrder(OrderValidator validator) { validator.checkStock(); } // 局部变量形式的依赖 public String generateReport() { DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE; return formatter.format(LocalDate.now()); } }关键特征:
- 生命周期:被依赖对象由外部创建,方法结束即解除关系
- Spring特性:适合用于不需要容器管理的工具类(如
StringUtils)
1.2 关联关系:稳定的伙伴关系
关联(Association)表现为类成员变量,在Spring中通常通过@Autowired实现:
@Entity public class User { @Id private Long id; // 单向关联 @OneToMany private List<Order> orders; } @Service public class PaymentService { // 通过@Autowired建立的关联 @Autowired private NotificationService notificationService; }对比表:
| 特性 | 依赖关系 | 关联关系 |
|---|---|---|
| 持续时间 | 方法执行期间 | 对象生命周期内 |
| 代码表现 | 方法参数/局部变量 | 成员变量 |
| Spring实现 | 直接new或静态调用 | @Autowired或JPA注解 |
| UML箭头 | 虚线箭头 → | 实线箭头 → |
提示:在DDD设计中,关联关系往往对应着领域模型中的核心关系
2. 聚合与组合:整体与部分的哲学
2.1 聚合关系:可独立存在的部分
聚合(Aggregation)用空心菱形表示,典型特征是部分可以脱离整体存在。电商系统中的经典案例:
public class ShoppingCart { private List<Product> products; // 商品可独立于购物车存在 public ShoppingCart(List<Product> products) { this.products = products; } } // 商品可以单独创建 Product phone = new Product("iPhone", 9999); ShoppingCart cart = new ShoppingCart(List.of(phone));Spring Boot中的体现:
- 通过构造函数注入的Bean
@Bean方法参数注入- JPA中的
@ManyToMany关系
2.2 组合关系:生死与共的绑定
组合(Composition)用实心菱形表示,部分不能脱离整体存在。在Spring中表现为:
public class Order { // 订单项随订单创建销毁 private List<OrderItem> items = new ArrayList<>(); public void addItem(Product p, int quantity) { items.add(new OrderItem(p, quantity)); // OrderItem由Order管理 } } // OrderItem不能单独存在 public class OrderItem { private Product product; private int quantity; OrderItem(Product product, int quantity) { this.product = product; this.quantity = quantity; } }生命周期对比:
聚合关系:
- 整体删除不影响部分
- 例如:删除购物车,商品仍存在
组合关系:
- 整体删除则部分随之删除
- 例如:删除订单,订单项必须级联删除
// Spring Data JPA中的组合关系映射 @Entity public class Order { @Id private Long id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private List<OrderItem> items; }3. Spring Bean生命周期中的UML关系实践
3.1 单例Bean与原型Bean的选择
@Configuration public class AppConfig { // 组合关系 - 紧密绑定 @Bean public OrderService orderService() { return new OrderService(orderValidator()); } @Bean public OrderValidator orderValidator() { return new OrderValidator(); } // 聚合关系 - 松散耦合 @Bean @Scope("prototype") public ReportGenerator reportGenerator() { return new ReportGenerator(); } }Bean作用域使用指南:
| 关系类型 | 推荐作用域 | 典型应用场景 |
|---|---|---|
| 组合 | Singleton | 核心服务如OrderService |
| 聚合 | Prototype | 有状态的工具类如ReportGenerator |
| 关联 | Singleton | 基础设施组件如EmailService |
| 依赖 | - | 静态工具类如StringUtils |
3.2 循环依赖的破解之道
双向关联在Spring中可能导致循环依赖问题,解决方案:
// 问题代码 - 可能导致启动失败 @Service public class UserService { @Autowired private OrderService orderService; } @Service public class OrderService { @Autowired private UserService userService; } // 解决方案1:使用@Lazy @Service public class OrderService { @Lazy @Autowired private UserService userService; } // 解决方案2:Setter注入 @Service public class OrderService { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } }4. 实战:电商系统领域模型设计
4.1 用户核心领域建模
// 组合关系 - 用户与其认证信息 @Entity public class User { @Id private Long id; @Embedded // 组合关系 private Credential credential; @OneToMany(mappedBy = "user") // 聚合关系 private List<Address> addresses; } @Embeddable public class Credential { private String encryptedPassword; private LocalDateTime passwordUpdatedAt; }4.2 订单处理状态模式
// 状态模式中的UML关系应用 public class Order { private OrderState state; // 组合关系 public void cancel() { state.handleCancel(); } } public interface OrderState { void handleCancel(); } // 具体状态类 public class PaidState implements OrderState { @Autowired // 依赖关系 private RefundService refundService; public void handleCancel() { refundService.process(/*...*/); } }架构建议:
- 核心领域用组合关系保证内聚性
- 跨模块协作用聚合关系降低耦合
- 工具类方法使用依赖关系
- 基础设施组件用关联关系
理解这些UML关系在Spring Boot中的实际表现,能帮助我们在微服务拆分时做出更合理的架构决策。当你在代码中明确看到两个类之间的关系符号时,应该能立即想到它在Spring上下文中的实现方式和生命周期影响。
