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

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

通過以前的4篇文章,我們看到Spring採用了大量的關於創建和結構方面的設計模式。本文將描述屬於行爲方面的兩種設計模式:命令和訪問者。

前傳:

命令模式

這篇文章描述的第一個行爲設計模式是命令。它允許將請求封裝在一個對象內並附加一個回調動作(每次遇到所所謂的回調大家就只需要理解爲一個函數方法就好,省的去浪費那麼多腦子)。請求被封裝在命令對象之下,而請求的結果被髮送到接收者。命令本身不是由調用者執行。爲了直白瞭解其中的主要思想,想象一下管理服務器的情況(遠程通過 ssh操作 Linux服務器)。管理員( invoker)在命令行( commands)中啓動一些操作,將結果發送到服務器(接收器)。在這裏,所有這一切都是由客戶端的終端(也就是我們用的 xshell)來完成的。搞個 Demo來說明一下(對於命令,它的動作就是執行,對於管理員來講,我們的動作其實就是一個回車,執不執行當然是管理員說的算了,執行交給命令對象了,服務器最後就是一個展示結果):

  1. 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;
    
     }
    
    
    
    }

     

     

測試應通過並打印兩個命令:

  1. 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中的兩段代碼:

  1. /**
    
        * 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註釋):

  1. 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測試用例:

  1. 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的實例)參數的訪問者模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章