聊聊Spring中最常用的十種設計模式
前言
作為一名有多年開發經驗的老司機,每次翻看Spring源碼都讓我感嘆:"這哪是框架,分明是設計模式的百科全書!"
有些小伙伴在工作中可能只會用@Autowired
,卻不知背后藏著多少精妙設計。
今天這篇文章跟大家一起聊聊Spring中最常用的10種設計模式,希望對你會有所幫助。
1.模板方法模式:流程骨架大師
場景:處理重復流程但允許細節變化Spring應用:JdbcTemplate
、RestTemplate
等
// 偽代碼展示模板方法核心
publicabstractclass JdbcTemplate {
// 定義算法骨架(不可重寫)
public final Object execute(String sql) {
Connection conn = getConnection(); // 抽象方法
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Object result = mapResult(rs); // 抽象方法
releaseResources(conn, stmt, rs);
return result;
}
// 留給子類實現的鉤子方法
protected abstract Connection getConnection();
protected abstract Object mapResult(ResultSet rs);
}
為什么用:
- 復用資源管理(連接獲取/釋放)等通用邏輯
- 允許子類只關注業務差異(如結果映射)思考:當你寫重復流程時,想想能否抽出模板骨架
2.工廠模式:對象出生管理局
場景:解耦對象創建與使用Spring應用:BeanFactory
核心接口
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(Class<T> requiredType);
}
// 實現類:DefaultListableBeanFactory
public class UserService {
// 使用者無需關心Bean如何創建
@Autowired
private OrderService orderService;
}
設計精髓:
- 隱藏復雜的對象初始化過程(如循環依賴處理)
- 統一管理對象生命周期(單例/原型等作用域)類比:就像點外賣不需要知道廚師怎么做菜
3.代理模式:隱形護衛
場景:無侵入增強對象功能
Spring應用:AOP動態代理
// JDK動態代理示例
publicclass LogProxy implements InvocationHandler {
private Object target;
public Object createProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
System.out.println("【日志】調用方法: " + method.getName());
return method.invoke(target, args); // 執行原方法
}
}
// Spring中通過@Aspect實現類似功能
@Aspect
@Component
publicclass LogAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logMethodCall(JoinPoint jp) {
System.out.println("調用方法: " + jp.getSignature().getName());
}
}
動態代理兩板斧:
- JDK代理:基于接口(要求目標類實現接口)
- CGLIB代理:基于繼承(可代理普通類)價值:業務邏輯與橫切關注點(日志/事務等)徹底解耦
4.單例模式:全局唯一指揮官
場景:減少資源消耗,保證全局一致性
Spring實現:Bean默認作用域
// 源碼片段:AbstractBeanFactory
public Object getBean(String name) {
Object bean = getSingleton(name); // 先查緩存
if (bean == null) {
bean = createBean(name); // 不存在則創建
addSingleton(name, bean); // 放入緩存
}
return bean;
}
關鍵設計:
- 三級緩存解決循環依賴(singletonObjects, earlySingletonObjects, singletonFactories)
- 并發安全通過
synchronized
+雙重檢查鎖定實現警示:切忌在單例Bean中保存狀態變量!
5.觀察者模式:事件廣播網
場景:解耦事件生產者和消費者
Spring應用:ApplicationEvent
機制
// 1. 定義事件
publicclass OrderCreatedEvent extends ApplicationEvent {
public OrderCreatedEvent(Order source) {
super(source);
}
}
// 2. 發布事件
@Service
publicclass OrderService {
@Autowired ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// 業務邏輯...
publisher.publishEvent(new OrderCreatedEvent(order));
}
}
// 3. 監聽事件
@Component
publicclass EmailListener {
@EventListener
public void handleOrderEvent(OrderCreatedEvent event) {
// 發送郵件通知
}
}
優勢:
- 事件源與監聽器完全解耦
- 支持異步處理(加
@Async
注解即可)
6.策略模式:算法切換器
場景:動態選擇算法實現
Spring應用:Resource
資源加載
// 資源加載策略族
Resource res1 = new ClassPathResource("config.xml"); // 類路徑策略
Resource res2 = new UrlResource("http://config.com");// 網絡策略
Resource res3 = new FileSystemResource("/opt/config");// 文件系統策略
// 統一調用接口
InputStream is = res1.getInputStream();
源碼設計亮點:
Resource
接口統一抽象- 通過
ResourceLoader
自動選擇策略應用場景:支付方式切換(微信/支付寶/銀聯)
7.適配器模式:接口轉換器
場景:兼容不兼容的接口
Spring應用:Spring MVC的HandlerAdapter
// 偽代碼:處理多種Controller
publicclass RequestMappingHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return handler instanceof Controller;
}
public ModelAndView handle(HttpRequest req, HttpResponse res, Object handler) {
Controller controller = (Controller) handler;
return controller.handleRequest(req, res); // 統一適配調用
}
}
// 實際Spring源碼中處理了:
// 1. @Controller注解類 2. HttpRequestHandler 3. Servlet實現等
價值:
- 讓DispatcherServlet無需關心Controller具體類型
- 新增Controller類型只需擴展適配器
8.裝飾器模式:功能增強包
場景:動態添加功能
Spring應用:HttpServletRequest
包裝
// 典型應用:緩存請求體
ContentCachingRequestWrapper wrappedRequest =
new ContentCachingRequestWrapper(rawRequest);
// 可在filter中多次讀取body
byte[] body = wrappedRequest.getContentAsByteArray();
源碼實現:
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedContent;
@Override
public ServletInputStream getInputStream() {
// 裝飾原方法:緩存流數據
}
}
設計本質:通過包裝器在不修改原對象基礎上增強功能
9.建造者模式:復雜對象組裝工
場景:分步構建復雜對象
Spring應用:BeanDefinitionBuilder
// 構建復雜的Bean定義
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
builder.addPropertyValue("maxRetry", 3);
builder.setInitMethodName("init");
builder.setScope(BeanDefinition.SCOPE_SINGLETON);
// 注冊到容器
registry.registerBeanDefinition("userService", builder.getBeanDefinition());
對比傳統構造:
- 解決多參數構造的混亂(尤其可選參數多時)
- 構建過程更加清晰可讀
10 責任鏈模式:攔截器的骨架設計
場景:解耦多步驟處理流程
Spring應用:HandlerInterceptor
攔截器鏈
// Spring MVC核心執行鏈
publicclass HandlerExecutionChain {
privatefinal List<HandlerInterceptor> interceptors = new ArrayList<>();
// 執行前置處理(責任鏈核心)
public boolean applyPreHandle(HttpServletRequest request,
HttpServletResponse response) {
for (int i = 0; i < interceptors.size(); i++) {
HandlerInterceptor interceptor = interceptors.get(i);
// 任意攔截器返回false則中斷鏈條
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, i); // 清理已完成
returnfalse;
}
}
returntrue;
}
}
實戰配置:
@Configuration
publicclass WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 構建責任鏈
registry.addInterceptor(new LogInterceptor()).order(1);
registry.addInterceptor(new AuthInterceptor()).order(2);
registry.addInterceptor(new RateLimitInterceptor()).order(3);
}
}
// 獨立攔截器實現
publicclass AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
if (!checkToken(req.getHeader("Authorization"))) {
res.sendError(401); // 認證失敗
returnfalse; // 中斷鏈
}
returntrue; // 放行
}
}
設計價值:
- 開閉原則:新增攔截器無需修改現有代碼
- 單一職責:每個攔截器只關注單一功能
- 動態編排:通過
order()
靈活調整執行順序 - 流程控制:任意節點可中斷或繼續傳遞
典型反模式:在攔截器中注入其他攔截器,這將破壞責任鏈獨立性,導致循環依賴!
總結
- 解耦的藝術工廠模式解耦創建/使用,觀察者模式解耦事件/處理
- 擴展性的智慧策略模式支持算法擴展,裝飾器模式支持功能擴展
- 復雜性的封裝模板方法封裝流程,建造者模式封裝構建
- 性能的權衡單例模式減少資源消耗,代理模式按需增強
最后送給小伙伴們的建議:不要為了用模式而用模式。
就像Spring的作者Rod Johnson說的:"優雅的代碼不是模式的堆砌,而是恰到好處的抽象。"