Spring框架中的設計模式(五)通過以前的4篇文章,我們看到Spring采用了大量的關于創(chuàng)建和結構方面的設計模式。
本文將描述屬于行為方面的兩種設計模式:命令和訪問者。
命令模式
這篇文章描述的第一個行為設計模式是命令。它允許將請求封裝在一個對象內并附加一個回調動作(每次遇到所所謂的回調大家就只需要理解為一個函數(shù)方法就好,省的去浪費那么多腦子)。請求被封裝在命令對象之下,而請求的結果被發(fā)送到接收者。命令本身不是由調用者執(zhí)行。為了直白了解其中的主要思想,想象一下管理服務器的情況(遠程通過ssh操作Linux服務器)。管理員(invoker)在命令行(commands)中啟動一些操作,將結果發(fā)送到服務器(接收器)。在這里,所有這一切都是由客戶端的終端(也就是我們用的xshell)來完成的。搞個Demo來說明一下(對于命令,它的動作就是執(zhí)行,對于管理員來講,我們的動作其實就是一個回車,執(zhí)不執(zhí)行當然是管理員說的算了,執(zhí)行交給命令對象了,服務器最后就是一個展示結果):
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)并將其傳輸?shù)浇邮掌鳎⊿erver),而且還可以更好地處理給定的請求。在這里,這種更好的處理是通過存儲命令的執(zhí)行歷史。在Spring中,我們在beanFactory后置處理器的特性中來找到指令設計模式的原理。要通過快速對它們進行定義,應用程序上下文會啟動后置處理器,并可以用來對創(chuàng)建的bean進行一些操作(這里不打算細說了,具體的我后面會專門寫一篇這方面的文章,來分析其中的源碼細節(jié))。
當我們將先前Demo里呈現(xiàn)的命令邏輯轉換并對比到Spring bean工廠后處理器時,我們可以區(qū)分以下actors:后置處理器bean(是指實現(xiàn)BeanFactoryPostProcessor接口)是命令,org.springframework.context.support.PostProcessorRegistrationDelegate是調用者(它執(zhí)行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里面得到對象(也就是為了得到一個命令的執(zhí)行結果),那么,想要在得到對象的時候就已經(jīng)實現(xiàn)了一些對其修改的想法,那么就通過后置處理器,也是就實現(xiàn)了后置處理器接口的beans(命令里可以通過傳入不同的參數(shù)來得到不同結果,或者對命令的腳本進行修改),然后還需要一個執(zhí)行者(我們在做自動化運維的時候,不止操作一個腳本,這里的PostProcessorRegistrationDelegate就是集中來管理這些的),最后得到的結果就由BeanFactory來展示咯。
訪問者模式
接下來要介紹的一個行為設計模式是Visitor:抽象一點就是通過另一種類型的對象來使一個對象訪問。在這個簡短定義中,使用這個設計模式中的對象將被視為訪問者或對象可被訪問。第一個訪問者要有可訪問支持。這個模式的一個現(xiàn)實的例子可以是一個汽車質檢員,他們檢查一些汽車零件,比如輪子,制動器和發(fā)動機,以判斷汽車質量是否合格。我們來做個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配置中實現(xiàn)了訪問者設計模式。為了觀察,我們可以看看org.springframework.beans.factory.config.BeanDefinitionVisitor對象,該對象用于解析bean元數(shù)據(jù)并將其解析為String(例如:具有作用域或工廠方法名稱的XML屬性)或Object(例如:構造函數(shù)定義中的參數(shù))。已解析的值在與分析的bean關聯(lián)的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的參數(shù),并將其替換為已解析對象。
在最后一篇關于Spring中設計模式的文章中,我們發(fā)現(xiàn)了2種行為模式:用于處理bean工廠的后置處理的命令模式和用于將定義的bean參數(shù)轉換為面向對象(String或Object的實例)參數(shù)的訪問者模式。