Spring框架中的設計模式(五)java
經過之前的4篇文章,咱們看到Spring採用了大量的關於建立和結構方面的設計模式。本文將描述屬於行爲方面的兩種設計模式:命令和訪問者。spring
前傳:shell
Spring框架中的設計模式(一)apache
Spring框架中的設計模式(三)tomcat
這篇文章描述的第一個行爲設計模式是命令。它容許將請求封裝在一個對象內並附加一個回調動做(每次遇到所所謂的回調你們就只須要理解爲一個函數方法就好,省的去浪費那麼多腦子)。請求被封裝在命令對象之下,而請求的結果被髮送到接收者。命令自己不是由調用者執行。爲了直白瞭解其中的主要思想,想象一下管理服務器的狀況(遠程經過 ssh
操做 Linux
服務器)。管理員( invoker
)在命令行( commands
)中啓動一些操做,將結果發送到服務器(接收器)。在這裏,全部這一切都是由客戶端的終端(也就是咱們用的 xshell
)來完成的。搞個 Demo
來講明一下(對於命令,它的動做就是執行,對於管理員來說,咱們的動做其實就是一個回車,執不執行固然是管理員說的算了,執行交給命令對象了,服務器最後就是一個展現結果):app
public class CommandTest { // This test method is a client @Test public void test() { Administrator admin = new Administrator(); Server server = new Server(); // start Apache admin.setCommand(new StartApache(server)); admin.typeEnter(); // start Tomcat admin.setCommand(new StartTomcat(server)); admin.typeEnter(); // check executed commands int executed = server.getExecutedCommands().size(); assertTrue("Two commands should be executed but only "+ executed+ " were", executed == 2); } } // commands abstract class ServerCommand { protected Server server; public ServerCommand(Server server) { this.server = server; } public abstract void execute(); } class StartTomcat extends ServerCommand { public StartTomcat(Server server) { super(server); } @Override public void execute() { server.launchCommand("sudo service tomcat7 start"); } } class StartApache extends ServerCommand { public StartApache(Server server) { super(server); } @Override public void execute() { server.launchCommand("sudo service apache2 start"); } } // invoker class Administrator { private ServerCommand command; public void setCommand(ServerCommand command) { this.command = command; } public void typeEnter() { this.command.execute(); } } // receiver class Server { // as in common terminals, we store executed commands in history private List<String> executedCommands = new ArrayList<String>(); public void launchCommand(String command) { System.out.println("Executing: "+command+" on server"); this.executedCommands.add(command); } public List<String> getExecutedCommands() { return this.executedCommands; } }
測試應經過並打印兩個命令:框架
Executing: sudo service apache2 start on server Executing: sudo service tomcat7 start on server
命令模式不只容許封裝請求(ServerCommand)並將其傳輸到接收器(Server),並且還能夠更好地處理給定的請求。在這裏,這種更好的處理是經過存儲命令的執行歷史。在Spring中,咱們在beanFactory後置處理器的特性中來找到指令設計模式的原理。要經過快速對它們進行定義,應用程序上下文會啓動後置處理器,並能夠用來對建立的bean進行一些操做(這裏不打算細說了,具體的我後面會專門寫一篇這方面的文章,來分析其中的源碼細節)。運維
當咱們將先前Demo裏呈現的命令邏輯轉換並對比到 Springbean工廠後處理器
時,咱們能夠區分如下 actors
:後置處理器bean(是指實現 BeanFactoryPostProcessor
接口)是命令,org.springframework.context.support.PostProcessorRegistrationDelegate是調用者(它執行 postProcessBeanFactory
方法註冊全部的後置處理器bean,此處看下面第二段代碼)和接收器org.springframework.beans.factory.config.ConfigurableListableBeanFactory能夠在元素(bean)構造初始化以前修改它們(例如:在初始化bean以前能夠更改屬性)。
另外,回顧下上面的那個Demo,和咱們的Demo中的命令歷史管理同樣。 PostProcessorRegistrationDelegate
包含一個內部類 BeanPostProcessorChecker
,它能夠記錄當一個bean不符合處理條件的狀況。
能夠觀察 PostProcessorRegistrationDelegate
中的兩段代碼:
/** * BeanPostProcessor that logs an info message when a bean is created during * BeanPostProcessor instantiation, i.e. when a bean is not eligible for * getting processed by all BeanPostProcessors. */ private static class BeanPostProcessorChecker implements BeanPostProcessor { private static final Log logger = LogFactory.getLog(BeanPostProcessorChecker.class); private final ConfigurableListableBeanFactory beanFactory; private final int beanPostProcessorTargetCount; public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) { this.beanFactory = beanFactory; this.beanPostProcessorTargetCount = beanPostProcessorTargetCount; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean != null && !(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { logger.info("Bean '" + beanName + "' of type [" + bean.getClass() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } } return bean; } private boolean isInfrastructureBean(String beanName) { if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) { BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName); return RootBeanDefinition.ROLE_INFRASTRUCTURE == bd.getRole(); } return false; } }
定義後的調用,用的就是 ConfigurableListableBeanFactory
的實例(看 BeanPostProcessorChecker
註釋):
public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); // Register BeanPostProcessorChecker that logs an info message when // a bean is created during BeanPostProcessor instantiation, i.e. when // a bean is not eligible for getting processed by all BeanPostProcessors. int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; //BeanPostProcessorChecker beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); // Separate between BeanPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<BeanPostProcessor> internalPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } // First, register the BeanPostProcessors that implement PriorityOrdered. sortPostProcessors(beanFactory, priorityOrderedPostProcessors); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // Next, register the BeanPostProcessors that implement Ordered. List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } sortPostProcessors(beanFactory, orderedPostProcessors); registerBeanPostProcessors(beanFactory, orderedPostProcessors); // Now, register all regular BeanPostProcessors. List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // Finally, re-register all internal BeanPostProcessors. sortPostProcessors(beanFactory, internalPostProcessors); registerBeanPostProcessors(beanFactory, internalPostProcessors); // Re-register post-processor for detecting inner beans as ApplicationListeners, // moving it to the end of the processor chain (for picking up proxies etc). beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); }
總結一個過程就是,我要BeanFactory裏面獲得對象(也就是爲了獲得一個命令的執行結果),那麼,想要在獲得對象的時候就已經實現了一些對其修改的想法,那麼就經過後置處理器,也是就實現了後置處理器接口的beans(命令裏能夠經過傳入不一樣的參數來獲得不一樣結果,或者對命令的腳本進行修改),而後還須要一個執行者(咱們在作自動化運維的時候,不止操做一個腳本,這裏的
PostProcessorRegistrationDelegate
就是集中來管理這些的),最後獲得的結果就由BeanFactory
來展現咯。
接下來要介紹的一個行爲設計模式是Visitor:抽象一點就是經過另外一種類型的對象來使一個對象訪問。在這個簡短定義中,使用這個設計模式中的對象將被視爲訪問者或對象可被訪問。第一個訪問者要有可訪問支持。這個模式的一個現實的例子能夠是一個汽車質檢員,他們檢查一些汽車零件,好比輪子,制動器和發動機,以判斷汽車質量是否合格。咱們來作個JUnit測試用例:
public class VisitorTest { @Test public void test() { CarComponent car = new Car(); Mechanic mechanic = new QualifiedMechanic(); car.accept(mechanic); assertTrue("After qualified mechanics visit, the car should be broken", car.isBroken()); Mechanic nonqualifiedMechanic = new NonQualifiedMechanic(); car.accept(nonqualifiedMechanic); assertFalse("Car shouldn't be broken becase non qualified mechanic " + " can't see breakdowns", car.isBroken()); } } // visitor interface Mechanic { public void visit(CarComponent element); public String getName(); } class QualifiedMechanic implements Mechanic { @Override public void visit(CarComponent element) { element.setBroken(true); } @Override public String getName() { return "qualified"; } } class NonQualifiedMechanic implements Mechanic { @Override public void visit(CarComponent element) { element.setBroken(true); } @Override public String getName() { return "unqualified"; } } // visitable abstract class CarComponent { protected boolean broken; public abstract void accept(Mechanic mechanic); public void setBroken(boolean broken) { this.broken = broken; } public boolean isBroken() { return this.broken; } } class Car extends CarComponent { private boolean broken = false; private CarComponent[] components; public Car() { components = new CarComponent[] { new Wheels(), new Engine(), new Brake() }; } @Override public void accept(Mechanic mechanic) { this.broken = false; if (mechanic.getName().equals("qualified")) { int i = 0; while (i < components.length && this.broken == false) { CarComponent component = components[i]; mechanic.visit(component); this.broken = component.isBroken(); i++; } } // if mechanic isn't qualified, we suppose that // he isn't able to see breakdowns and so // he considers the car as no broken // (even if the car is broken) } @Override public boolean isBroken() { return this.broken; } } class Wheels extends CarComponent { @Override public void accept(Mechanic mechanic) { mechanic.visit(this); } } class Engine extends CarComponent { @Override public void accept(Mechanic mechanic) { mechanic.visit(this); } } class Brake extends CarComponent { @Override public void accept(Mechanic mechanic) { mechanic.visit(this); } }
在這個例子中,咱們能夠看到他們有兩個機制(訪問者,其實就是免檢和難免檢):合格和不合格。暴露於他們的可見對象是汽車。經過其接受方式,決定哪一個角色應該適用於被訪問者(經過代碼 mechanic.getName().equals("qualified")
來判斷)。當訪問者合格時,Car讓他分析全部組件。若是訪問者不合格,Car認爲其干預是無用的,而且在方法 isBroken()
中直接返回 false
(其實就是爲了達到一個免檢的效果)。 Spring在beans配置中實現了訪問者設計模式
。爲了觀察,咱們能夠看看org.springframework.beans.factory.config.BeanDefinitionVisitor對象,該對象用於 解析bean元數據
並將其解析爲 String
(例如:具備做用域或工廠方法名稱的XML屬性)或 Object
(例如:構造函數定義中的參數)。已解析的值在與分析的bean關聯的 BeanDefinition
實例中進行判斷設置。具體的源碼請看 BeanDefinitionVisitor
的代碼片斷:
/** * Traverse the given BeanDefinition object and the MutablePropertyValues * and ConstructorArgumentValues contained in them. * @param beanDefinition the BeanDefinition object to traverse * @see #resolveStringValue(String) */ public void visitBeanDefinition(BeanDefinition beanDefinition) { visitParentName(beanDefinition); visitBeanClassName(beanDefinition); visitFactoryBeanName(beanDefinition); visitFactoryMethodName(beanDefinition); visitScope(beanDefinition); visitPropertyValues(beanDefinition.getPropertyValues()); ConstructorArgumentValues cas = beanDefinition. getConstructorArgumentValues(); visitIndexedArgumentValues(cas. getIndexedArgumentValues()); visitGenericArgumentValues(cas. getGenericArgumentValues()); } protected void visitParentName(BeanDefinition beanDefinition) { String parentName = beanDefinition.getParentName(); if (parentName != null) { String resolvedName = resolveStringValue(parentName); if (!parentName.equals(resolvedName)) { beanDefinition.setParentName(resolvedName); } } }
在這種狀況下,他們只是訪問方式,沒有對訪問者作任何補充的控制(在Demo裏對car的質檢員作了控制)。這裏訪問包括分析給定 BeanDefinition
的參數,並將其替換爲已解析對象。
在最後一篇關於Spring中設計模式的文章中,咱們發現了2種行爲模式: 用於處理bean工廠的後置處理的命令模式
和 用於將定義的bean參數轉換爲面向對象(String或Object的實例)參數的訪問者模式
。