Spring框架中的設計模式(五)

Spring框架中的設計模式(二)

在上一篇中我們在Spring中所談到的設計模式涉及到了創建模式三劍客和1個行為模式(解釋器模式)。這次我們會將眼光更多地關注在具有結構性和行為性的設計模式上。

在這篇文章中,我們將看到每個類型的兩種模式。首先將關注類型是的結構設計模式。它將包含代理和複合。下一個將介紹行為模式:策略和模板方法。

代理模式

面向對象編程(OOP)可能是編程中最流行的概念。然而,Spring引入了另一種編碼規範,面向切面編程(AOP)。為了簡化定義,AOP是面向系統特定點的一種編程,如:異常拋出,特定類別方法的執行等.AOP允許在執行這些特定點之前或之後執行補充動作。如何實現這種操作?它可以通過監聽器(listeners)進行。但在這種情況下,我們應該在只要可能存在調用的地方都需要定義監聽器來進行監聽(比如在一個方法的開始的地方)。這就是為什麼Spring不採用這個idea。相反,Spring實現了一種能夠通過額外的方法調用完成任務的設計模式

- 代理設計模式

代理就像對象的鏡像一樣。也正因為如此,代理對象不僅可以覆蓋真實對象,還可以擴展其功能。因此,對於只能在屏幕上列印一些文本的對象,我們可以添加另一個對象來過濾顯示單詞。可以通過代理來定義第二個對象的調用。代理是封裝真實對象的對象。例如,如果您嘗試調用Waiter

bean,那麼您將調用該Bean的代理,其行為方式完全相同。

代理設計模式的一個很好的例子是org.springframework.aop.framework.ProxyFactoryBean。該工廠根據Spring bean構建AOP代理。該類實現了定義getObject()方法的FactoryBean介面。此方法用於將需求Bean的實例返回給bean factory。在這種情況下,它不是返回的實例,而是AOP代理。在執行代理對象的方法之前,可以通過調用補充方法來進一步「修飾」代理對象(其實所謂的靜態代理不過是在裝飾模式上加了個要不要你來干動作行為而已,而不是裝飾模式什麼也不做就加了件衣服,其他還得由你來全權完成)。

ProxyFactory的一個例子是:

public class TestProxyAop { @Test public void test() { ProxyFactory factory = new ProxyFactory(new House()); factory.addInterface(Construction.class); factory.addAdvice(new BeforeConstructAdvice()); factory.setExposeProxy(true); Construction construction = (Construction) factory.getProxy(); construction.construct(); assertTrue("Construction is illegal. " + "Supervisor didnt give a permission to build " + "the house", construction.isPermitted()); }}

interface Construction { public void construct(); public void givePermission(); public boolean isPermitted();}

class House implements Construction{ private boolean permitted = false; @Override public boolean isPermitted() { return this.permitted; } @Override public void construct() { System.out.println("Im constructing a house"); } @Override public void givePermission() { System.out.println("Permission is given to construct a simple house"); this.permitted = true; }}

class BeforeConstructAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] arguments, Object target) throws Throwable { if (method.getName().equals("construct")) { ((Construction) target).givePermission(); } } }

這個測試應該通過,因為我們不直接在House實例上操作,而是代理它。代理調用第一個BeforeConstructAdvice的before方法(指向在執行目標方法之前執行,在我們的例子中為construct())通過它,給出了一個「許可權」來構造對象的欄位(house)。代理層提供了一個額外新功能,因為它可以簡單地分配給另一個對象。要做到這一點,我們只能在before方法之前修改過濾器。

複合模式

另一種結構模式是複合模式。在關於Spring中設計模式)的第一篇文章中,我們使用構建器來構造複雜對象。另一種實現方法是使用複合模式。這種模式是基於具有共同行為的多個對象的存在,用於構建更大的對象。較大的對象仍然具有與最小對象相同的特徵。那麼用它來定義相同的行為。

複合對象的非Spring示例可以是一個寫入HTML的文本對象,由包含span或em標籤的段落組成:

public class CompositeTest { @Test public void test() { TextTagComposite composite = new PTag(); composite.addTag(new SpanTag()); composite.addTag(new EmTag()); // sample client code composite.startWrite(); for (TextTag leaf : composite.getTags()) { leaf.startWrite(); leaf.endWrite(); } composite.endWrite(); assertTrue("Composite should contain 2 tags but it contains "+composite.getTags().size(), composite.getTags().size() == 2); }}

interface TextTag { public void startWrite(); public void endWrite();}

interface TextTagComposite extends TextTag { public List<TextTag> getTags(); public void addTag(TextTag tag);}

class PTag implements TextTagComposite { private List<TextTag> tags = new ArrayList<TextTag>(); @Override public void startWrite() { System.out.println("<p>"); } @Override public void endWrite() { System.out.println("</p>"); } @Override public List<TextTag> getTags() { return tags; } @Override public void addTag(TextTag tag) { tags.add(tag); }}

class SpanTag implements TextTag { @Override public void startWrite() { System.out.println("<span>"); } @Override public void endWrite() { System.out.println("</span>"); }}

class EmTag implements TextTag { @Override public void startWrite() { System.out.println("<em>"); } @Override public void endWrite() { System.out.println("</em>"); }}

在這種情況下,可以看到一個複合對象。我們可以區分複合與非複合對象,因為第一個可以容納一個或多個非複合對象(PTag類中的private List tags欄位)。非複合對象稱為葉子。TextTag介面被稱為組件,因為它為兩個對象類型提供了共同的行為規範(有點像Linux文件管理系統的有共同點的文件放在一個文件夾下進行管理,其實就是節點管理)。

在Spring世界中,我們檢索複合對象的概念是org.springframework.beans.BeanMetadataElement介面,用於配置bean對象。它是所有繼承對象的基本界面。現在,在一方面,我們有一個葉子,由org.springframework.beans.factory.parsing.BeanComponentDefinition表示,另一邊是複合org.springframework.beans.factory.parsing.CompositeComponentDefinition。CompositeComponentDefinition類似於組件,因為它包含addNestedComponent(ComponentDefinition component)方法,它允許將葉添加到私有final列表中nestedComponents。您可以看到,由於此列表,BeanComponentDefinition和CompositeComponentDefinition的組件是org.springframework.beans.factory.parsing.ComponentDefinition

策略模式

本文描述的第三個概念是策略設計模式。策略定義了通過不同方式完成相同事情的幾個對象。完成任務的方式取決於採用的策略。舉個例子說明,我們可以去一個國家。我們可以乘公共汽車,飛機,船甚至汽車去那裡。所有這些方法將把我們運送到目的地國家。但是,我們將通過檢查我們的銀行帳戶來選擇最適應的方式。如果我們有很多錢,我們將採取最快的方式(可能是私人飛行)。如果我們沒有足夠的話,我們會採取最慢的(公車,汽車)。該銀行賬戶作為確定適應策略的因素。

Spring在org.springframework.web.servlet.mvc.multiaction.MethodNameResolver類(過時,但不影響拿來研究)中使用策略設計模式。它是MultiActionController(同樣過時)的參數化實現。在開始解釋策略之前,我們需要了解MultiActionController的實用性。這個類允許同一個類處理幾種類型的請求。作為Spring中的每個控制器,MultiActionController執行方法來響應提供的請求。策略用於檢測應使用哪種方法。解析過程在MethodNameResolver實現中實現,例如在同一個包中的ParameterMethodNameResolver中。方法可以通過多個條件解決:屬性映射,HTTP請求參數或URL路徑。

@Overridepublic String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { String methodName = null; // Check parameter names where the very existence of each parameter // means that a method of the same name should be invoked, if any. if (this.methodParamNames != null) { for (String candidate : this.methodParamNames) { if (WebUtils.hasSubmitParameter(request, candidate)) { methodName = candidate; if (logger.isDebugEnabled()) { logger.debug("Determined handler method " + methodName + " based on existence of explicit request parameter of same name"); } break; } } } // Check parameter whose value identifies the method to invoke, if any. if (methodName == null && this.paramName != null) { methodName = request.getParameter(this.paramName); if (methodName != null) { if (logger.isDebugEnabled()) { logger.debug("Determined handler method " + methodName + " based on value of request parameter " + this.paramName + ""); } } } if (methodName != null && this.logicalMappings != null) { // Resolve logical name into real method name, if appropriate. String originalName = methodName; methodName = this.logicalMappings.getProperty(methodName, methodName); if (logger.isDebugEnabled()) { logger.debug("Resolved method name " + originalName + " to handler method " + methodName + ""); } } if (methodName != null && !StringUtils.hasText(methodName)) { if (logger.isDebugEnabled()) { logger.debug("Method name " + methodName + " is empty: treating it as no method name found"); } methodName = null; } if (methodName == null) { if (this.defaultMethodName != null) { // No specific method resolved: use default method. methodName = this.defaultMethodName; if (logger.isDebugEnabled()) { logger.debug("Falling back to default handler method " + this.defaultMethodName + ""); } } else { // If resolution failed completely, throw an exception. throw new NoSuchRequestHandlingMethodException(request); } } return methodName;}

正如我們在前面的代碼中可以看到的,方法的名稱通過提供的參數映射,URL中的預定義屬性或參數存在來解決(默認情況下,該參數的名稱是action)。

模板模式

本文提出的最後一個設計模式是模板方法。此模式定義了類行為的骨架,並將子步驟的某些步驟的延遲執行(具體就是下面例子中一個方法放在另一個方法中,只有調用另一方方法的時候這個方法才會執行,而且還可能會在其他行為方法之後按順序執行)。其中寫了一種方法(下面例子中的construct()),注意定義為final,起著同步器的角色。它以給定的順序執行由子類定義的方法。在現實世界中,我們可以將模板方法與房屋建設進行比較。獨立於建造房屋的公司,我們需要從建立基礎開始,只有在我們完成之後才能做其他的工作。這個執行邏輯將被保存在一個我們不能改變的方法中。例如基礎建設或刷牆會被作為一個模板方法中的方法,具體到建築房屋的公司。我們可以在給定的例子中看到它:

public class TemplateMethod { public static void main(String[] args) { HouseAbstract house = new SeaHouse(); house.construct(); }}abstract class HouseAbstract { protected abstract void constructFoundations(); protected abstract void constructWall(); // template method public final void construct() { constructFoundations(); constructWall(); }}class EcologicalHouse extends HouseAbstract { @Override protected void constructFoundations() { System.out.println("Making foundations with wood"); } @Override protected void constructWall() { System.out.println("Making wall with wood"); }}

class SeaHouse extends HouseAbstract { @Override protected void constructFoundations() { System.out.println("Constructing very strong foundations"); } @Override protected void constructWall() { System.out.println("Constructing very strong wall"); }}

該代碼應該輸出:

Constructing very strong foundationsConstructing very strong wall

Spring在org.springframework.context.support.AbstractApplicationContext類中使用模板方法。他們不是一個模板方法(在我們的例子中是construct ),而是多個。例如,getsFreshBeanFactory返回內部bean工廠的新版本,調用兩個抽象方法:refreshBeanFactory(刷新工廠bean)和getBeanFactory(以獲取更新的工廠bean)。這個方法和其他一些方法一樣,用在public void refresh()中,拋出構造應用程序上下文的BeansException,IllegalStateException方法(這裡會在後面Spring中與應用程序上下文分析中再次提到)。

我們可以從同一個包中的GenericApplicationContext找到一些通過模板方法所實現的抽象方法的實現的例子(說的有點拗口,多讀幾遍就好):

/** * Do nothing: We hold a single internal BeanFactory and rely on callers * to register beans through our public methods (or the BeanFactorys). * @see #registerBeanDefinition */@Overrideprotected final void refreshBeanFactory() throws IllegalStateException { if (this.refreshed) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts: just call refresh once"); } this.beanFactory.setSerializationId(getId()); this.refreshed = true;}@Overrideprotected void cancelRefresh(BeansException ex) { this.beanFactory.setSerializationId(null); super.cancelRefresh(ex);}/** * Not much to do: We hold a single internal BeanFactory that will never * get released. */@Overrideprotected final void closeBeanFactory() { this.beanFactory.setSerializationId(null);}/** * Return the single internal BeanFactory held by this context * (as ConfigurableListableBeanFactory). */@Overridepublic final ConfigurableListableBeanFactory getBeanFactory() { return this.beanFactory;}/** * Return the underlying bean factory of this context, * available for registering bean definitions. * <p><b>NOTE:</b> You need to call {@link #refresh()} to initialize the * bean factory and its contained beans with application context semantics * (autodetecting BeanFactoryPostProcessors, etc). * @return the internal bean factory (as DefaultListableBeanFactory) */public final DefaultListableBeanFactory getDefaultListableBeanFactory() { return this.beanFactory;}

經過上面這些可以讓我們發現Spring如何通過使用行為和結構設計模式來更好地組織上下文(模板方法),並通過相應策略來解決執行方法。它使用兩種結構設計模式,通過代理模式來簡化AOP部分並通過複合模式來構造複雜對象。

有問題可以加qq群523409180 討論的


推薦閱讀:

MyBatis不是完整的ORM框架?
Spring Security(二) -- Spring Security的Filter
web工程父子容器
利用Spring載入參數配置文件
Spring MVC @ModelAttribute詳解

TAG:Spring | 設計模式 |