1. 为什么Spring Boot开发者必须掌握设计模式
在Spring Boot应用开发中,设计模式就像建筑师的蓝图,它们提供了经过验证的解决方案框架。我见过太多项目因为缺乏模式思维而陷入混乱——过度耦合的组件、难以扩展的功能、重复造轮子的实现。掌握设计模式能让你写出更优雅、更易维护的代码。
Spring框架本身就是设计模式的集大成者。从控制反转(IoC)容器的单例模式,到AOP的代理模式,再到事件机制的观察者模式,设计模式贯穿Spring架构的每个角落。而Spring Boot作为Spring的"约定优于配置"实现,更是将这些模式的使用简化到了极致。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Spring Boot中的九大核心设计模式详解
2.1 单例模式(Singleton) - Bean的默认作用域
在Spring Boot中,单例模式的应用随处可见。通过@Bean注解注册的组件默认就是单例:
java复制@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
Spring容器会确保整个应用生命周期内myService实例只有一个。这种设计极大减少了对象创建开销,但也带来了线程安全问题。我曾在高并发场景下踩过坑——共享的单例Bean中使用了非线程安全的HashMap导致数据错乱。解决方案要么改用ConcurrentHashMap,要么调整Bean作用域为原型(prototype)。
提示:单例Bean中要特别注意成员变量的线程安全性。推荐使用ThreadLocal或方法局部变量来避免并发问题。
2.2 工厂模式(Factory) - BeanFactory与@Bean
Spring的核心容器本质上就是一个超级工厂。我们常用的ApplicationContext就是BeanFactory的增强版。开发者可以通过多种方式定义Bean:
- XML配置(传统方式)
@Component及其衍生注解(@Service,@Repository等)@Bean方法(适合第三方库的集成)
工厂模式的精髓在于将对象创建逻辑封装起来。比如数据库连接池的创建:
java复制@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
这样业务代码只需注入DataSource,无需关心具体实现是HikariCP还是Druid。
2.3 代理模式(Proxy) - AOP的基石
Spring AOP的实现严重依赖代理模式。当你在方法上添加@Transactional注解时:
java复制@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 业务逻辑
}
}
Spring会在运行时创建代理对象,在方法调用前后加入事务管理逻辑。这实际上是代理模式的典型应用——在不修改原始类的情况下增强功能。
我遇到过的一个典型问题是:同一个类内部方法调用不会触发AOP代理。这是因为this.method()调用的是真实对象而非代理对象。解决方案要么重构代码,要么使用AspectJ的编译时织入。
2.4 模板方法模式(Template Method) - JdbcTemplate等
Spring的JdbcTemplate、RestTemplate等类都是模板方法模式的典范。以JdbcTemplate.query()为例:
java复制public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return execute(sql, (Statement stmt) -> {
ResultSet rs = stmt.executeQuery(sql);
List<T> results = new ArrayList<>();
while (rs.next()) {
results.add(rowMapper.mapRow(rs, rs.getRow()));
}
return results;
});
}
开发者只需关注RowMapper这个可变部分(结果集映射),而连接获取、异常处理等固定流程由模板处理。这种模式大幅减少了样板代码。
2.5 观察者模式(Observer) - 事件机制
Spring的事件机制完美实现了观察者模式:
java复制// 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
public OrderCreatedEvent(Order source) {
super(source);
}
}
// 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// 业务逻辑
publisher.publishEvent(new OrderCreatedEvent(order));
}
}
// 监听事件
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理逻辑
}
}
这种松耦合的设计让系统各组件能够灵活交互。我曾用这种模式实现了一个审计日志系统——任何业务操作触发事件后,审计模块自动记录日志,完全不影响主业务流程。
2.6 适配器模式(Adapter) - HandlerAdapter
在Spring MVC中,HandlerAdapter是适配器模式的典型应用。不同的Controller可能有不同的写法:
- 基于
@Controller注解的方式 - 实现
Controller接口的方式 - 基于函数式编程的方式
HandlerAdapter将这些差异封装起来,对DispatcherServlet提供统一的调用接口。这让我想起曾经做过的老系统迁移——通过编写自定义Adapter,我们成功让新旧两套Controller实现共存于同一系统中。
2.7 装饰器模式(Decorator) - HttpInputMessage
Spring中HttpInputMessage的实现就使用了装饰器模式。比如ContentCachingRequestWrapper可以对原始请求进行装饰,添加缓存功能:
java复制public class CachingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
filterChain.doFilter(wrappedRequest, response);
}
}
这样后续处理流程可以多次读取请求体,而原始请求只能读取一次。装饰器模式在不改变接口的前提下扩展了功能,这种设计在中间件开发中特别有用。
2.8 策略模式(Strategy) - 多环境配置
Spring Profile就是策略模式的体现。我们可以为不同环境定义不同的配置策略:
java复制@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
应用启动时通过spring.profiles.active参数选择具体策略。我在微服务架构中经常使用这种模式——同一套代码在Kubernetes和本地开发环境表现出完全不同的行为。
2.9 责任链模式(Chain of Responsibility) - 过滤器与拦截器
Spring MVC的过滤器链和拦截器链都是责任链模式的实现。每个过滤器或拦截器都有机会处理请求,也可以决定是否传递给下一个处理器:
java复制@Component
public class LoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) {
logRequest(request);
chain.doFilter(request, response);
logResponse(response);
}
}
我曾用这种模式实现了一个复杂的权限校验系统——不同级别的校验器组成责任链,每个校验器专注于单一职责,最终完成全面的权限控制。
3. 设计模式在Spring Boot中的组合应用
真正的系统设计往往是多种模式的组合运用。以电商系统中的订单服务为例:
- 使用工厂模式创建订单处理器(根据订单类型不同)
- 处理器内部使用策略模式计算折扣
- 订单创建后发布观察者模式的事件
- 事件监听器使用代理模式添加事务管理
- 订单查询使用模板方法模式封装数据库操作
这种模式组合让系统既灵活又规范。我在重构一个遗留系统时,通过引入这些模式,将原本3000行的God Class拆解成了多个职责单一的小类,维护成本降低了70%。
4. 避免设计模式的常见误用
虽然设计模式很强大,但滥用反而会让代码更难理解。我见过最典型的反模式包括:
-
过度工程化:在简单场景使用复杂模式。比如只有一种支付方式时使用策略模式。
-
模式嵌套过深:多层代理+装饰器+工厂的组合,导致调试困难。
-
违背模式初衷:比如单例模式本应控制实例数量,却通过反射创建多个实例。
我的经验法则是:当模式能让代码更简单而不是更复杂时才使用。KISS原则(Keep It Simple, Stupid)永远应该是首要考虑。
5. Spring Boot 3.x中的模式新变化
随着Spring Boot 3.x的发布,一些模式的应用方式也有了新变化:
-
函数式Bean注册:更灵活的工厂模式实现
java复制@Configuration public class AppConfig { @Bean public RouterFunction<ServerResponse> router(OrderHandler handler) { return route() .GET("/orders", handler::listOrders) .build(); } } -
GraalVM原生镜像支持:对代理模式的影响较大,需要额外配置
-
新的事件模型:观察者模式有了更高效的实现
我在迁移Spring Boot 2.x到3.x时发现,理解这些底层模式的变化能大幅减少升级过程中的兼容性问题。
