切換語言為:簡體

【程式設計師進階教程】Spring 原始碼中的 16 種設計模式實現

  • 爱糖宝
  • 2024-06-17
  • 2092
  • 0
  • 0

Spring Framework 是一個龐大而複雜的框架,它涵蓋了多個模組和元件,每個元件都有其獨特的功能和作用。小編一直建議同學們在學習 Spring 時需要學習 Spring 的原始碼,說句實話,Spring 原始碼太 TM 優秀了,你不僅可以理解原理,還能在學習 Spring 原始碼的過程中,學到設計模式的應用,以下就是小編在學習 Spring 原始碼時整理的16種設計模式應用的地方,分享給你。

Spring 原始碼實現中使用了許多常見的設計模式,這些設計模式幫助 Spring 框架實現了靈活、可擴充套件和可維護的特性。以下是 Spring 原始碼中常見的設計模式及其解釋:

1、工廠模式(Factory Pattern)

在 Spring 中,BeanFactory 和 ApplicationContext 是工廠模式的一個典型應用。它們負責根據配置資訊或註解來建立和管理 bean 例項,隱藏了具體物件的建立細節,提供了一種靈活的方式來管理物件的生命週期。

BeanFactory:

抽象工廠角色:BeanFactory 介面是抽象工廠角色,它定義了獲取和管理 bean 物件的方法。

具體工廠角色:DefaultListableBeanFactory 類是 BeanFactory 介面的一個具體實現類,它負責具體的 bean 建立和管理工作。

角色分析:實現程式碼: 在 DefaultListableBeanFactory 類中,透過 ConcurrentHashMap 來儲存 bean 的定義資訊,即 bean 名稱與對應的 BeanDefinition 物件之間的對映關係。當需要獲取 bean 時,透過呼叫 getBean(String name) 方法,容器會根據名稱從 beanDefinitionMap 中獲取對應的 BeanDefinition物件,並根據 BeanDefinition 的配置資訊建立或獲取 bean 例項。

ApplicationContext:

抽象工廠角色:ApplicationContext 介面繼承了 BeanFactory 介面,除了提供了獲取和管理 bean 物件的方法外,還提供了更多的企業級特性,如國際化、事件釋出、資源載入等。

具體工廠角色:AbstractApplicationContext 類是 ApplicationContext 介面的一個具體實現類,它繼承了 DefaultListableBeanFactory,同時還實現了 ApplicationEventPublisher、MessageSource、ResourceLoader 等介面,提供了額外的功能。

角色分析:實現程式碼: AbstractApplicationContext 類在初始化過程中會載入所有的 bean 定義並建立 BeanFactory 例項,同時會對其他功能進行初始化,如事件監聽器的註冊、訊息源的載入等。當需要獲取 bean 時,可以透過呼叫 getBean(String name) 方法獲取對應的 bean 例項。此外,AbstractApplicationContext 還提供了其他方法來支援事件釋出、訊息獲取等功能。

2、單例模式(Singleton Pattern)

Spring 中的 BeanFactory 和 ApplicationContext 預設情況下都是單例的,即容器中的 bean 預設為單例物件。這樣可以確保在整個應用程式中只有一個例項存在,節省了系統資源並提高了效能。BeanFactory 和 ApplicationContext 實現單例模式的機制略有不同。我們來看看它們的原始碼如何實現單例模式:

BeanFactory

在 BeanFactory 中,單例模式的實現主要依賴於快取機制。BeanFactory 使用一個 ConcurrentHashMap 來快取已經建立的單例物件。

public Object getBean(String name) {
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(name);
        if (singletonObject == null) {
            singletonObject = createBean(name);
            this.singletonObjects.put(name, singletonObject);
        }
        return singletonObject;
    }
}


當呼叫 getBean() 方法獲取 bean 例項時,BeanFactory 會首先嚐試從快取中獲取,如果快取中存在對應的單例物件,則直接返回;如果快取中不存在,則根據 bean 的定義資訊建立新的例項物件,並放入快取中。

public Object getBean(String name) {
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(name);
        if (singletonObject == null) {
            singletonObject = createBean(name);
            this.singletonObjects.put(name, singletonObject);
        }
        return singletonObject;
    }
}


ApplicationContext

ApplicationContext 也使用了類似的快取機制來實現單例模式,不過相比於 BeanFactory,ApplicationContext 會在容器啟動時預先例項化所有的單例 bean,並將其放入快取中,以便在應用程式執行期間能夠快速獲取到單例物件。

// AbstractApplicationContext.java

// 建立 singletonObjects 快取
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 預載入所有單例 bean
protected void refreshBeanFactory() throws BeansException {
    // ...
    // 預先例項化所有的 singleton bean
    preInstantiateSingletons();
}

// 預先例項化所有 singleton bean
protected void preInstantiateSingletons() throws BeansException {
    // 獲取 bean 定義的名稱列表
    String[] beanNames = getBeanDefinitionNames();
    // 遍歷所有的 bean 定義
    for (String beanName : beanNames) {
        // 獲取 bean 的定義資訊
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        // 判斷是否是單例 bean
        if (bd.isSingleton()) {
            // 建立單例 bean 例項並放入快取中
            Object bean = getBean(beanName);
            this.singletonObjects.put(beanName, bean);
        }
    }
}


BeanFactory 和 ApplicationContext 實現單例模式的機制都是基於快取的方式,透過快取已經建立的單例物件來確保每次呼叫 getBean() 方法獲取單例物件時都返回同一個例項。這樣可以節省系統資源,並且保證了單例物件的一致性。

3、代理模式(Proxy Pattern)

Spring AOP 中的代理模式是實現切面功能的關鍵。Spring 使用代理物件來實現切面功能,它可以在方法呼叫前後執行額外的邏輯,如日誌記錄、效能監控等。

在 Spring 框架中,AOP(面向切面程式設計)的實現主要依賴於代理模式。Spring 使用動態代理來實現 AOP,主要有兩種代理方式:基於 JDK 動態代理和基於 CGLIB 動態代理。

下面簡要介紹 Spring 原始碼中如何使用代理模式實現 AOP:

基於 JDK 動態代理的 AOP:

當目標物件實現了介面時,Spring 使用 JDK 動態代理來建立 AOP 代理物件。在 JDK 動態代理中,代理物件實現了目標物件的所有介面,並將方法呼叫委託給 MethodInvocation 介面的實現類來處理。

Spring 原始碼中主要涉及到以下幾個類和介面:

java.lang.reflect.Proxy:JDK 提供的動態代理類,用於建立代理物件。

org.springframework.aop.framework.JdkDynamicAopProxy:Spring 中用於基於 JDK 動態代理的 AOP 代理物件生成器。

org.springframework.aop.framework.ProxyFactory:Spring 中用於建立 AOP 代理物件的工廠類。

基於 CGLIB 動態代理的 AOP:

當目標物件沒有實現介面時,Spring 使用 CGLIB 動態代理來建立 AOP 代理物件。在 CGLIB 動態代理中,代理物件繼承了目標物件,並重寫了目標物件的方法,以便在方法呼叫前後執行額外的邏輯。

Spring 原始碼中主要涉及到以下幾個類和介面:

org.springframework.cglib.proxy.Enhancer:CGLIB 提供的動態代理類,用於建立代理物件。

org.springframework.aop.framework.CglibAopProxy:Spring 中用於基於 CGLIB 動態代理的 AOP 代理物件生成器。

無論是基於 JDK 動態代理還是基於 CGLIB 動態代理,Spring 都提供了統一的 AOP API,即 org.springframework.aop 包下的一系列介面和類,包括切面、通知、連線點等,以及各種 AOP 相關的配置方式,如 XML 配置、註解配置等。

Spring 框架透過代理模式實現了 AOP,使得開發者可以方便地在應用程式中新增和管理切面邏輯,實現了橫切關注點的解耦和重用。

4、裝飾器模式(Decorator Pattern)

Spring 的 BeanPostProcessor 介面就是裝飾器模式的一個應用。透過實現 BeanPostProcessor 介面,可以在 bean 例項化後、初始化前後動態地新增額外的處理邏輯。

在 Spring 原始碼中,BeanPostProcessor 介面的實現是基於裝飾者模式的,它允許我們在 bean 例項化、依賴注入和初始化階段對 bean 進行額外的處理,而不需要修改原始的 bean 類。

下面是 Spring 原始碼中如何使用裝飾者模式實現 BeanPostProcessor:

BeanPostProcessor 介面定義:

首先,我們來看一下 BeanPostProcessor 介面的定義:

package org.springframework.beans.factory.config;

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

BeanPostProcessor 介面定義了兩個方法:postProcessBeforeInitialization 和 postProcessAfterInitialization。它們允許我們在 bean 初始化前後對 bean 進行額外的處理。

AbstractApplicationContext 類:

在 Spring 原始碼中,AbstractApplicationContext 類實現了 BeanFactory 和 ApplicationContext 介面,並且提供了對 BeanPostProcessor 的支援。

package org.springframework.context.support;

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {
    // ...
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // ...
        // 註冊 BeanPostProcessor,對 bean 進行裝飾
        for (BeanPostProcessor processor : beanFactory.getBeanPostProcessors()) {
            beanFactory.addBeanPostProcessor(processor);
        }
        // ...
    }
    // ...
}


在 AbstractApplicationContext 類中,透過呼叫 finishBeanFactoryInitialization 方法,會註冊所有的 BeanPostProcessor 例項到 BeanFactory 中,從而對 bean 進行裝飾。

AbstractAutowireCapableBeanFactory 類:

最後,我們來看一下 AbstractAutowireCapableBeanFactory 類,它是 DefaultListableBeanFactory 類的父類,負責實現了 BeanFactory 的核心功能。

package org.springframework.beans.factory.support;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    // ...
    protected void invokeAwareMethods(final String beanName, final Object bean) {
        // ...
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof Aware) {
                if (processor instanceof BeanNameAware) {
                    ((BeanNameAware) processor).setBeanName(beanName);
                }
                // ...
            }
        }
        // ...
    }
    // ...
}


在 AbstractAutowireCapableBeanFactory 類中,透過呼叫 invokeAwareMethods 方法,會遍歷所有的 BeanPostProcessor 例項,如果發現實現了特定的 Aware 介面(如 BeanNameAware),則呼叫其對應的方法來設定 bean 的相關屬性。

Spring 原始碼中使用裝飾者模式實現了 BeanPostProcessor 介面,允許我們在 bean 例項化、依賴注入和初始化階段對 bean 進行額外的處理,從而實現了更靈活、可擴充套件的 bean 處理機制。

5、觀察者模式(Observer Pattern)

Spring 的事件機制是觀察者模式的一個應用。透過定義事件和監聽器,可以實現物件之間的解耦,當事件發生時通知所有註冊的監聽器進行處理。

在 Spring 原始碼中,觀察者模式被廣泛應用於實現事件機制。Spring 的事件機制允許應用程式中的元件監聽特定型別的事件,並在事件發生時執行相應的邏輯。下面是 Spring 原始碼中如何使用觀察者模式實現事件機制的簡要說明:

(1)事件類:

Spring 中的事件通常由具體的事件類表示,這些事件類通常繼承自 ApplicationEvent 類。每個事件類通常包含與事件相關的資訊,例如事件源、時間戳等。

在 Spring 中,事件類通常繼承自 ApplicationEvent 類,例如 ContextRefreshedEvent、ContextStartedEvent 等。這些事件類包含了與事件相關的資訊。

(2)事件監聽器介面:

Spring 提供了一個 ApplicationListener 介面,用於監聽特定型別的事件。監聽器實現該介面,並透過泛型指定要監聽的事件型別。

Spring 提供了 ApplicationListener 介面,用於監聽特定型別的事件。事件監聽器實現該介面,並實現 onApplicationEvent() 方法來處理事件。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}


(3)事件釋出器:

Spring 提供了一個 ApplicationEventPublisher 介面,用於釋出事件。ApplicationContext 介面繼承了 ApplicationEventPublisher,因此 ApplicationContext 是事件釋出器的一個實現。

ApplicationContext 介面繼承了 ApplicationEventPublisher 介面,因此 ApplicationContext 是事件釋出器的一個實現。透過呼叫 publishEvent() 方法來發布事件。

public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
}


(4)事件監聽器註冊:

在 Spring 中,事件監聽器可以透過兩種方式進行註冊:

透過在 Spring 配置檔案中配置監聽器 bean。

透過程式碼註冊監聽器。

事件監聽器可以透過在 Spring 配置檔案中配置監聽器 bean,或者透過程式碼註冊監聽器來進行註冊。

(5)事件釋出:

當某個事件發生時,透過事件釋出器釋出該事件。事件釋出器會將事件傳送給所有註冊的事件監聽器。

context.publishEvent(new MyCustomEvent(this));


(6)事件處理:

事件監聽器收到事件後,會執行相應的事件處理邏輯。每個事件監聽器可以根據需要監聽多個不同型別的事件。

小結:Spring 原始碼中使用觀察者模式實現了事件機制,透過事件類、事件監聽器介面、事件釋出器和事件監聽器註冊等元件,使得應用程式中的元件可以監聽特定型別的事件,並在事件發生時執行相應的邏輯。這種設計實現了元件之間的解耦和靈活的事件處理機制。

6、策略模式(Strategy Pattern)

Spring 中的各種策略介面和實現類,如 ResourceLoader、Environment、EnvironmentCapable 等,都是策略模式的應用。它們允許使用者根據不同的需求選擇不同的實現方式。

在 Spring 原始碼中,ResourceLoader、Environment 和 EnvironmentCapable 都是使用策略模式實現的。策略模式允許在執行時動態地選擇演算法或行為。下面分別介紹這三個介面在 Spring 原始碼中的實現方式:

ResourceLoader

ResourceLoader 介面定義了資源載入器的行為,它允許應用程式在執行時動態地載入資源。在 Spring 原始碼中,ResourceLoader 介面的實現主要有 ClassPathResourceLoader、FileSystemResourceLoader 和 UrlResourceLoader。

ClassPathResourceLoader:用於從類路徑載入資源。

FileSystemResourceLoader:用於從檔案系統載入資源。

UrlResourceLoader:用於從 URL 地址載入資源。

這些實現類透過實現 ResourceLoader 介面,提供了不同的資源載入策略,從而使得應用程式能夠根據需要選擇不同的資源載入方式。

Environment 和 EnvironmentCapable:

Environment 介面用於表示應用程式的環境,它提供了一種統一的方式來獲取環境相關的資訊,如屬性值、配置檔案等。EnvironmentCapable 介面用於表示具有環境能力的元件,即可以獲取到 Environment 物件的元件。

在 Spring 原始碼中,Environment 介面的實現主要有 StandardEnvironment、MutablePropertySources 和 PropertySource 等。而 EnvironmentCapable 介面的實現主要是 ApplicationContext 介面。

StandardEnvironment:提供了標準的環境實現,用於獲取系統屬性、環境變數、配置檔案等。

MutablePropertySources:用於管理屬性源,即配置檔案、環境變數等來源。

PropertySource:表示屬性源,可以是配置檔案、環境變數等。

這些實現類透過實現 Environment 介面和 EnvironmentCapable 介面,提供了統一的獲取環境資訊的策略,使得應用程式能夠靈活地獲取環境相關的資訊,並根據需要選擇不同的環境配置。

小結:Spring 原始碼中使用策略模式實現了 ResourceLoader、Environment 和 EnvironmentCapable 介面,透過提供不同的實現類來提供不同的資源載入策略和環境獲取策略,使得應用程式能夠根據需要選擇不同的實現方式,並實現了元件之間的解耦。

7、模板方法模式(Template Method Pattern)

Spring 中的 JdbcTemplate 等是模板方法模式的應用。它們定義了執行資料庫操作的標準流程,但將具體的操作委託給子類來實現。

在 Spring 原始碼中,JdbcTemplate 使用了模板方法模式(Template Method Pattern)來簡化資料庫訪問操作,封裝了常見的資料庫操作流程,提供了模板方法供開發者使用,同時留出了部分方法供子類進行具體實現,以滿足不同的資料庫訪問需求。下面簡要介紹 Spring 原始碼中如何使用模板方法模式實現 JdbcTemplate:

(1)JdbcTemplate 類結構:

JdbcTemplate 類是 Spring JDBC 模組中的核心類,用於簡化 JDBC 操作。它包含了一系列模板方法,如 query、update、batchUpdate 等,以及一些回撥方法供開發者實現。

(2)模板方法模式在 JdbcTemplate 中的應用:

JdbcTemplate 類中的模板方法主要是 query、update、batchUpdate 等方法,它們定義了執行資料庫操作的標準流程,但具體的操作實現由子類或者回調函式來完成。

(3)具體實現類:

在 JdbcTemplate 中,具體的資料庫操作實現由 RowMapper、PreparedStatementSetter、ResultSetExtractor 等介面的實現類來完成。這些介面的實現類負責將 JDBC 操作所需的引數設定、結果集對映等具體邏輯進行實現。

(4)模板方法:

JdbcTemplate 類中的模板方法包含了一系列常見的資料庫操作,如 query、update、batchUpdate 等。這些模板方法定義了資料庫操作的流程,並提供了回撥方法供開發者實現自定義的邏輯。

(5)示例程式碼:

下面是使用 JdbcTemplate 進行資料庫查詢的示例程式碼:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
List<User> users = jdbcTemplate.query(
    "SELECT * FROM users",
    (resultSet, rowNum) -> {
        User user = new User();
        user.setId(resultSet.getLong("id"));
        user.setUsername(resultSet.getString("username"));
        user.setEmail(resultSet.getString("email"));
        return user;
    }
);


在這個示例中,query 方法是 JdbcTemplate 中的一個模板方法,它定義了執行查詢操作的標準流程,但具體的結果集對映由回撥函式 (resultSet, rowNum) -> {...} 完成,這就是模板方法模式的應用。

8、介面卡模式(Adapter Pattern)

Spring MVC 中的 HandlerAdapter 就是介面卡模式的應用。它允許不同型別的處理器(Controller)以統一的方式來處理請求,透過介面卡將不同型別的處理器適配成統一的處理器介面。

在 Spring 原始碼中,HandlerAdapter 介面的實現是透過介面卡模式實現的。HandlerAdapter 介面定義了處理器介面卡的行為,它允許 Spring MVC 框架呼叫不同型別的處理器(Controller、HandlerMethod 等)並執行相應的處理邏輯。下面是 Spring 原始碼中如何使用介面卡模式實現 HandlerAdapter 的簡要說明:

HandlerAdapter 介面定義:

HandlerAdapter 介面定義了對處理器的呼叫方式,一般包括多個方法,如 supports() 方法用於判斷介面卡是否支援某種型別的處理器,handle() 方法用於執行處理器。

public interface HandlerAdapter {
    boolean supports(Object handler);

    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}


具體介面卡類:

Spring 原始碼中提供了多個具體的介面卡類,用於適配不同型別的處理器,如 SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter 等。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public boolean supports(Object handler) {
        return (handler instanceof SimpleController);
    }

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 處理 SimpleController 型別的處理器
    }
}


具體的介面卡類根據支援的處理器型別來判斷是否支援某種型別的處理器,並實現相應的處理邏輯。

DispatcherServlet 中的呼叫:

在 DispatcherServlet 中,根據請求的處理器型別選擇合適的 HandlerAdapter 來執行處理器。

protected ModelAndView haHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha.handle(request, response, handler);
        }
    }
    return null;
}


DispatcherServlet 會遍歷所有註冊的 HandlerAdapter,找到支援當前處理器型別的介面卡,並呼叫其 handle() 方法執行處理器。

透過介面卡模式,Spring 實現了 HandlerAdapter 介面的多種具體介面卡類,每個介面卡類負責將不同型別的處理器適配到 HandlerAdapter 介面上,從而統一處理器的呼叫方式,提高了處理器的複用性和靈活性。

9、建造者模式(Builder Pattern)

在 Spring 中,透過建造者模式可以簡化複雜物件的構建過程。比如,Spring 中的 RestTemplateBuilder 就是建造者模式的應用,用於構建 RestTemplate 例項。

在 Spring 原始碼中,RestTemplate 類的建立使用了建造者模式(Builder Pattern)。建造者模式允許逐步構建複雜物件,並且使得構建過程更加靈活,同時可以保證物件的一致性。下面是 Spring 原始碼中如何使用建造者模式實現 RestTemplate 的簡要說明:

RestTemplateBuilder 類:

Spring 原始碼中提供了 RestTemplateBuilder 類,用於構建 RestTemplate 物件。RestTemplateBuilder 類實現了建造者模式,它提供了一系列方法用於設定 RestTemplate 的屬性和特性,並最終構建出一個完整的 RestTemplate 物件。

示例程式碼:

下面是使用 RestTemplateBuilder 建立 RestTemplate 物件的示例程式碼:

RestTemplate restTemplate = new RestTemplateBuilder()
        .rootUri("https://api.example.com")
        .basicAuthentication("username", "password")
        .build();


在這個示例中,RestTemplateBuilder 提供了 rootUri()、basicAuthentication() 等方法用於設定 RestTemplate 的屬性,最後呼叫 build() 方法構建出一個 RestTemplate 物件。

RestTemplateBuilder 類原始碼:

RestTemplateBuilder 類的原始碼如下所示:

public class RestTemplateBuilder {
    private String rootUri;
    private CredentialsProvider credentialsProvider;

    public RestTemplateBuilder rootUri(String rootUri) {
        this.rootUri = rootUri;
        return this;
    }

    public RestTemplateBuilder basicAuthentication(String username, String password) {
        this.credentialsProvider = new BasicCredentialsProvider();
        this.credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        return this;
    }

    public RestTemplate build() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(this.rootUri));
        if (this.credentialsProvider != null) {
            restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom().setDefaultCredentialsProvider(this.credentialsProvider).build()));
        }
        return restTemplate;
    }
}


在 RestTemplateBuilder 類中,透過鏈式呼叫一系列方法來設定 RestTemplate 的屬性,並在 build() 方法中構建出一個 RestTemplate 物件。在 build() 方法中,根據設定的屬性建立 RestTemplate 物件,並設定相應的請求工廠、URI 模板處理器等屬性。

透過使用建造者模式,Spring 實現了 RestTemplate 的建立過程,使得構建過程更加靈活,同時保證了 RestTemplate 物件的一致性和可複用性。

10、訪問者模式(Visitor Pattern)

Spring 中的 BeanDefinitionVisitor 介面和其實現類是訪問者模式的應用。它可以遍歷訪問容器中的所有 BeanDefinition 物件,並執行相應的操作。

Spring 原始碼中使用了訪問者模式來實現 BeanDefinitionVisitor。BeanDefinitionVisitor 用於訪問 BeanDefinition 樹形結構中的每個節點,並執行特定的操作。下面是 Spring 原始碼中如何使用訪問者模式實現 BeanDefinitionVisitor 的簡要說明:

BeanDefinitionVisitor 介面:

Spring 提供了 BeanDefinitionVisitor 介面,用於定義訪問者的行為。

public interface BeanDefinitionVisitor {
    void visitBeanDefinition(BeanDefinition beanDefinition);
}


BeanDefinitionVisitor 介面定義了 visitBeanDefinition() 方法,用於訪問 BeanDefinition 樹中的每個節點。

AbstractBeanDefinitionVisitor 抽象類:

Spring 提供了 AbstractBeanDefinitionVisitor 抽象類,實現了 BeanDefinitionVisitor 介面,並提供了預設的 visitBeanDefinition() 方法實現。在遍歷 BeanDefinition 樹時,AbstractBeanDefinitionVisitor 類會遞迴呼叫 visitBeanDefinition() 方法。

public abstract class AbstractBeanDefinitionVisitor implements BeanDefinitionVisitor {
    public void visitBeanDefinition(BeanDefinition beanDefinition) {
        // 遍歷 BeanDefinition 樹,遞迴呼叫 visitBeanDefinition() 方法
        for (BeanDefinition bd : beanDefinition.getNestedBeanDefinitions()) {
            visitBeanDefinition(bd);
        }
    }
}


AbstractBeanDefinitionVisitor 類的主要作用是提供了預設的遍歷邏輯,遍歷 BeanDefinition 樹並遞迴呼叫 visitBeanDefinition() 方法。

具體的訪問者類:

在具體的應用場景中,開發者可以根據需要實現自定義的訪問者類,並實現 visitBeanDefinition() 方法來執行特定的操作。例如,可以實現一個輸出 BeanDefinition 資訊的訪問者類:

public class PrintingBeanDefinitionVisitor extends AbstractBeanDefinitionVisitor {
    public void visitBeanDefinition(BeanDefinition beanDefinition) {
        System.out.println("Bean name: " + beanDefinition.getBeanName());
        System.out.println("Bean class: " + beanDefinition.getBeanClassName());
        // 輸出其他 BeanDefinition 資訊...
        super.visitBeanDefinition(beanDefinition);
    }
}


在這個示例中,PrintingBeanDefinitionVisitor 類繼承了 AbstractBeanDefinitionVisitor 類,重寫了 visitBeanDefinition() 方法,並在方法中輸出了 BeanDefinition 的相關資訊。

透過使用訪問者模式,Spring 實現了 BeanDefinitionVisitor 介面的多種具體訪問者類,每個訪問者類負責執行不同的操作,使得開發者可以根據需要靈活地實現對 BeanDefinition 樹的訪問和操作。

11、責任鏈模式(Chain of Responsibility Pattern)

Spring Security 中的過濾器鏈就是責任鏈模式的一個應用。每個過濾器都可以選擇是否處理當前的請求,如果處理,則將請求傳遞給下一個過濾器,如果不處理,則將請求返回給呼叫者。

在 Spring Security 原始碼中,過濾器鏈的實現主要使用了責任鏈模式。Spring Security 中的過濾器鏈負責處理 Web 請求,並按照一定的順序依次呼叫不同的過濾器來完成安全認證、授權等操作。下面是 Spring Security 中如何使用責任鏈模式實現過濾器鏈的簡要說明:

Filter 介面:

Spring Security 中的過濾器都實現了 Filter 介面,它定義了過濾器的基本行為。

public interface Filter {
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
}


Filter 介面中的 doFilter() 方法用於處理請求,並將請求傳遞給下一個過濾器或目標資源。

FilterChain 介面:

Spring Security 中的過濾器鏈由 FilterChain 介面來管理,它定義了過濾器鏈的執行順序。

public interface FilterChain {
    void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}


FilterChain 介面中的 doFilter() 方法用於執行過濾器鏈中的下一個過濾器或目標資源。

責任鏈模式的實現:

在 Spring Security 中,過濾器鏈的實現使用了責任鏈模式,即將多個過濾器連結成一條鏈,每個過濾器依次處理請求,並將請求傳遞給下一個過濾器或目標資源。這樣,每個過濾器只需要關注自己的業務邏輯,而不需要關心其他過濾器的存在。

FilterChainProxy 類:

在 Spring Security 中,FilterChainProxy 類是過濾器鏈的關鍵實現。它負責管理所有的過濾器鏈,並按照一定的順序呼叫這些過濾器來處理請求。

public class FilterChainProxy extends GenericFilterBean {
    private List<SecurityFilterChain> filterChains;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 遍歷所有的過濾器鏈,按照順序呼叫過濾器
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                chain.doFilter(request, response);
                return;
            }
        }
        // 如果沒有匹配的過濾器鏈,則呼叫預設的 FilterChain 繼續處理請求
        chain.doFilter(request, response);
    }
}


在 FilterChainProxy 類中,透過遍歷所有的過濾器鏈,按照順序呼叫過濾器來處理請求。如果找到了匹配的過濾器鏈,則呼叫該過濾器鏈來處理請求;如果沒有找到匹配的過濾器鏈,則呼叫預設的 FilterChain 繼續處理請求。

Spring Security 原始碼中使用責任鏈模式實現了過濾器鏈,透過將多個過濾器連結成一條鏈,並按照一定的順序呼叫這些過濾器來處理請求,實現了安全認證、授權等功能。責任鏈模式使得每個過濾器都可以獨立地處理請求,並將請求傳遞給下一個過濾器或目標資源,從而提高了程式碼的靈活性和可維護性。

12、狀態模式(State Pattern)

Spring 中的 Bean 生命週期管理就是狀態模式的一個應用。Bean 可以處於不同的狀態,如初始化中、可用、銷燬中等,容器根據不同的狀態執行相應的操作。

在 Spring 原始碼中,並沒有顯式地使用狀態模式來實現 Bean 生命週期管理。但是,Spring 的 Bean 生命週期管理確實可以看作是一種狀態機模式的實現。在 Spring 中,每個 Bean 都有其生命週期,包括初始化、使用和銷燬等階段,而 Spring 容器負責管理這些生命週期。雖然 Spring 沒有定義一個專門的狀態機類來管理 Bean 生命週期,但是它的生命週期管理機制確實可以視為一種狀態機模式的實現。

以下是 Spring 如何管理 Bean 生命週期的簡要說明:

Bean 生命週期階段:

Spring 中的 Bean 生命週期包括多個階段,主要包括例項化、屬性賦值、初始化、使用和銷燬等階段。

Bean 生命週期回撥方法:

在 Spring 中,Bean 的生命週期由一系列回撥方法控制,這些回撥方法可以由開發者自定義並在 Bean 的不同生命週期階段被 Spring 容器呼叫。其中包括:

afterPropertiesSet() 方法:在所有屬性設定完成之後,Spring 容器會呼叫該方法來執行一些初始化操作。

init-method 屬性:可以在 XML 配置檔案中使用 init-method 屬性定義 Bean 的初始化方法。

destroy() 方法:在 Bean 被銷燬之前,Spring 容器會呼叫該方法執行一些清理操作。

destroy-method 屬性:可以在 XML 配置檔案中使用 destroy-method 屬性定義 Bean 的銷燬方法。

Spring 容器的生命週期管理:

Spring 容器負責管理 Bean 的生命週期,包括例項化、初始化、使用和銷燬等階段。Spring 容器在初始化 Bean 時會呼叫相應的初始化方法,而在銷燬 Bean 時會呼叫相應的銷燬方法。

雖然 Spring 原始碼中並沒有顯式地使用狀態模式來管理 Bean 生命週期,但是它的生命週期管理機制可以視為一種狀態機模式的實現。在這個實現中,每個 Bean 都處於不同的生命週期階段,並且在不同的階段可能會有不同的行為,而 Spring 容器負責管理這些狀態之間的轉換和相應的行為。因此,儘管 Spring 原始碼中沒有專門的狀態模式的實現類,但是它的生命週期管理機制確實可以被視為一種狀態機模式的實現。

13、迭代器模式(Iterator Pattern)

Spring 中的 BeanDefinition 介面的實現類(如 RootBeanDefinition、GenericBeanDefinition 等)使用了迭代器模式,可以遍歷訪問 BeanDefinition 中的各個屬性。

在 Spring 原始碼中,BeanDefinition 的集合通常是透過 List、Map 或其他集合型別來儲存的,而對這些集合的遍歷操作通常是透過迭代器模式來實現的。迭代器模式允許客戶端程式碼以統一的方式訪問集合中的元素,而無需瞭解底層集合的具體實現細節。下面是 Spring 原始碼中如何使用迭代器模式實現對 BeanDefinition 的遍歷:

BeanDefinitionRegistry 介面:

Spring 中的 BeanDefinitionRegistry 介面定義了對 BeanDefinition 的註冊和檢索方法,如 registerBeanDefinition()、getBeanDefinition() 等。其中,BeanDefinitionRegistry 介面還提供了獲取所有 BeanDefinition 的方法。

public interface BeanDefinitionRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
    BeanDefinition getBeanDefinition(String beanName);
    boolean containsBeanDefinition(String beanName);
    int getBeanDefinitionCount();
    String[] getBeanDefinitionNames();
}


BeanDefinitionIterator 實現:

Spring 原始碼中通常會有一個 BeanDefinitionIterator 類來實現對 BeanDefinition 的迭代操作。這個類通常是一個迭代器模式的實現,它封裝了對 BeanDefinition 集合的遍歷邏輯,並提供了統一的訪問介面。

public class BeanDefinitionIterator implements Iterator<BeanDefinition> {
    private final BeanDefinitionRegistry registry;
    private final String[] beanDefinitionNames;
    private int index = 0;

    public BeanDefinitionIterator(BeanDefinitionRegistry registry) {
        this.registry = registry;
        this.beanDefinitionNames = registry.getBeanDefinitionNames();
    }

    public boolean hasNext() {
        return index < beanDefinitionNames.length;
    }

    public BeanDefinition next() {
        String beanName = beanDefinitionNames[index];
        BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
        index++;
        return beanDefinition;
    }

    public void remove() {
        throw new UnsupportedOperationException("remove");
    }
}


在 BeanDefinitionIterator 類中,透過實現 Iterator 介面,封裝了對 BeanDefinition 集合的遍歷邏輯,提供了 hasNext()、next() 等方法用於訪問 BeanDefinition。

使用示例:

下面是使用 BeanDefinitionIterator 類遍歷 BeanDefinition 集合的示例程式碼:

BeanDefinitionRegistry registry = ...; // 獲取 BeanDefinitionRegistry 物件
BeanDefinitionIterator iterator = new BeanDefinitionIterator(registry);
while (iterator.hasNext()) {
    BeanDefinition beanDefinition = iterator.next();
    // 處理 BeanDefinition
}


在這個示例中,透過 BeanDefinitionIterator 類遍歷了 BeanDefinition 集合,並執行了相應的操作。

透過使用迭代器模式,Spring 實現了對 BeanDefinition 集合的統一遍歷操作,使得客戶端程式碼可以以統一的方式訪問 BeanDefinition,而無需瞭解底層集合的具體實現細節。這種方式提高了程式碼的靈活性和可維護性。

14、命令模式(Command Pattern)

Spring MVC 中的 HandlerMapping 就是命令模式的一個應用。它將請求對映到相應的處理器(Controller),並且支援不同型別的對映策略。

在 Spring 原始碼中,HandlerMapping 的實現通常使用了命令模式(Command Pattern)。HandlerMapping 的作用是根據請求的 URL 對映到相應的處理器(Handler),而命令模式可以將請求的處理過程封裝為一個命令物件,從而使得請求的處理過程與呼叫方解耦。下面是 Spring 原始碼中如何使用命令模式實現 HandlerMapping 的簡要說明:

HandlerMapping 介面:

Spring 提供了 HandlerMapping 介面,用於定義請求的對映規則,並將請求對映到相應的處理器。

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

HandlerMapping 介面定義了一個 getHandler() 方法,用於根據請求獲取相應的處理器。

HandlerExecutionChain 類:

Spring 中的 HandlerExecutionChain 類用於封裝請求的處理器及其攔截器鏈。

public class HandlerExecutionChain {
    private Object handler;
    private List<HandlerInterceptor> interceptors;

    // Getter 和 Setter 方法...
}

HandlerExecutionChain 類包含了一個 handler 屬性,用於儲存請求的處理器,以及一個 interceptors 屬性,用於儲存攔截器鏈。

具體命令類:

Spring 中通常會有多個具體的命令類來實現不同的 HandlerMapping 策略。每個具體的命令類負責根據不同的對映規則將請求對映到相應的處理器。

public class SimpleUrlHandlerMapping implements HandlerMapping {
    private Map<String, Object> urlMap;

    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        Object handler = urlMap.get(lookupPath);
        if (handler == null) {
            return null;
        }
        HandlerExecutionChain chain = new HandlerExecutionChain(handler);
        chain.addInterceptors(getInterceptors(lookupPath, handler));
        return chain;
    }

    // 其他方法...
}

在這個示例中,SimpleUrlHandlerMapping 類實現了 HandlerMapping 介面,並根據 URL 對映規則將請求對映到相應的處理器。

呼叫者:

在 Spring 中,DispatcherServlet 充當了呼叫者的角色,它會根據請求選擇合適的 HandlerMapping,並呼叫其 getHandler() 方法來獲取處理器。

public class DispatcherServlet extends HttpServlet {
    private List<HandlerMapping> handlerMappings;

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerExecutionChain handler = null;
        for (HandlerMapping hm : this.handlerMappings) {
            handler = hm.getHandler(request);
            if (handler != null) {
                break;
            }
        }
        if (handler == null) {
            // 未找到合適的處理器
            return;
        }
        // 執行攔截器鏈和處理器
        handler.applyPreHandle(request, response);
        Object handlerObj = handler.getHandler();
        // 呼叫處理器...
        handler.applyPostHandle(request, response, mv);
        handler.triggerAfterCompletion(request, response, ex);
    }
}

DispatcherServlet 類在執行請求時會遍歷所有的 HandlerMapping,並呼叫其 getHandler() 方法獲取處理器。

透過使用命令模式,Spring 實現了 HandlerMapping 的抽象,將請求的處理過程封裝為命令物件,使得請求的處理過程與呼叫方解耦。這種方式提高了程式碼的靈活性和可維護性。

15、直譯器模式(Interpreter Pattern)

Spring EL(Expression Language)就是直譯器模式的一個應用。Spring EL 可以解析和執行字串表示式,支援動態求值和型別轉換等功能。

在 Spring 原始碼中,Spring EL(表示式語言)的實現並沒有直接使用直譯器模式。但是,Spring EL 的實現可以被視為一種直譯器模式的應用。Spring EL 是一個功能強大的表示式語言,它允許在執行時對物件進行查詢和操作,並且可以嵌入到 Spring 框架的各個地方,如 XML 配置檔案、註解、SpEL 註解等。下面簡要介紹 Spring EL 的實現方式:

Spring EL 表示式解析:

Spring EL 表示式通常由一個表示式解析器(Expression Parser)來解析和執行。表示式解析器負責將表示式字串解析成相應的表示式物件,並執行該表示式以獲取結果。

SpEL(Spring Expression Language):

在 Spring 中,Spring EL 的主要實現是 SpEL(Spring Expression Language)。SpEL 提供了一個 ExpressionParser 介面,用於解析表示式,並提供了一個 EvaluationContext 介面,用於執行表示式。

ExpressionParser 介面:

ExpressionParser 介面定義了將表示式字串解析為表示式物件的方法。

public interface ExpressionParser {
    Expression parseExpression(String expressionString);
}

EvaluationContext 介面:

EvaluationContext 介面定義了執行表示式的方法,並提供了對變數、函式等上下文資訊的訪問。

public interface EvaluationContext {
    Object lookupVariable(String name);
    // 其他方法...
}

SpelExpressionParser 類:

Spring 中的 SpelExpressionParser 類實現了 ExpressionParser 介面,用於解析 SpEL 表示式。SpelExpressionParser 類將表示式字串解析為 SpelExpression 物件,並執行該表示式以獲取結果。

public class SpelExpressionParser implements ExpressionParser {
    public Expression parseExpression(String expressionString) {
        return new SpelExpression(expressionString);
    }
}

SpelExpression 類:

SpelExpression 類表示一個 SpEL 表示式,它封裝了表示式字串,並提供了執行表示式的方法。

public class SpelExpression implements Expression {
    private final String expressionString;

    public SpelExpression(String expressionString) {
        this.expressionString = expressionString;
    }

    public Object getValue(EvaluationContext context) throws EvaluationException {
        // 執行表示式,並返回結果
    }

    // 其他方法...
}

透過使用 SpEL,Spring 實現了一個功能強大的表示式語言,它允許在執行時對物件進行查詢和操作,並且可以嵌入到 Spring 框架的各個地方。雖然 Spring EL 的實現並沒有直接使用直譯器模式,但是它的執行過程可以被視為一種直譯器模式的應用,即將表示式字串解析為表示式物件,並執行該表示式以獲取結果。

16、備忘錄模式(Memento Pattern)

Spring 中的狀態儲存和恢復機制(如事務管理中的儲存點)就是備忘錄模式的一個應用。它可以儲存物件的內部狀態,並在需要時將物件恢復到之前的狀態。

在 Spring 原始碼中,並沒有顯式地使用備忘錄模式來實現狀態儲存和恢復機制。但是,Spring 中的一些功能確實可以被視為備忘錄模式的一種應用,尤其是在 AOP(面向切面程式設計)和事務管理等方面。

在 AOP 中,Spring 使用切面(Aspect)來捕獲方法呼叫,並在方法呼叫前後執行一些附加操作。這些附加操作通常包括儲存方法呼叫前的狀態,執行方法呼叫,然後根據需要恢復到之前的狀態。儘管 Spring 原始碼中並沒有專門的備忘錄模式的實現類,但是 AOP 中的這種狀態儲存和恢復機制可以被視為一種備忘錄模式的應用。

在事務管理中,Spring 使用事務切面來管理事務的開啟、提交、回滾等操作。在進行事務管理時,Spring 會儲存當前的資料庫連線狀態、事務狀態等資訊,並在事務執行完畢後根據需要進行恢復。雖然 Spring 原始碼中也沒有專門的備忘錄模式的實現類,但是事務管理中的這種狀態儲存和恢復機制也可以被視為備忘錄模式的一種應用。

雖然 Spring 原始碼中並沒有直接使用備忘錄模式來實現狀態儲存和恢復機制,但是在 AOP 和事務管理等功能中,Spring 使用了類似備忘錄模式的機制來儲存和恢復物件的狀態,從而提供了更加靈活和可靠的功能。


0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.