From the previous four articles, we’ve seen that Spring employs a number of design patterns in terms of creation and structure.

This article describes two design patterns that are behavioral: command and visitor.

Command mode

The first behavior design pattern described in this article is command. It allows the request to be wrapped in an object and attached with a callback action (each callback is understood as a function method, saving so much brainpower). The request is encapsulated under a command object, and the result of the request is sent to the receiver. The command itself is not executed by the caller. To see the main idea in plain view, imagine managing a server (remotely operating a Linux server over SSH). The administrator (Invoker) initiates some operations on the command line and sends the results to the server (receiver). In this case, all of this is done by the client-side terminal, which we use as xshell. Make a Demo to illustrate (for the command, its action is to execute, for the administrator, our action is actually a carriage return, execute or not execute of course is the administrator said forget it, execute to the command object, the server is finally a show result) :

public class CommandTest {

  // This test method is a client
  @Test
  public void test(a) {
    Administrator admin = new Administrator(a);
    Server server = new Server(a);

    // start Apache
    admin.setCommand(new StartApache(server));
    admin.typeEnter(a);

    // start Tomcat
    admin.setCommand(new StartTomcat(server));
    admin.typeEnter(a);

    // check executed commands
    int executed = server.getExecutedCommands().size(a);
    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(a);
}

class StartTomcat extends ServerCommand {

  public StartTomcat(Server server) {
    super(server);
  }

  @Override
  public void execute(a) {
    server.launchCommand("sudo service tomcat7 start");
  }
}

class StartApache extends ServerCommand {

  public StartApache(Server server) {
    super(server);
  }

  @Override
  public void execute(a) {
    server.launchCommand("sudo service apache2 start");
  }
}

// invoker
class Administrator {

  private ServerCommand command;

  public void setCommand(ServerCommand command) {
    this.command = command;
  }

  public void typeEnter(a) {
    this.command.execute(a);
  }

}

// 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(a) {
    return this.executedCommands;
  }

}
Copy the code

The test should pass and print two commands:

Executing: sudo service apache2 start on server
Executing: sudo service tomcat7 start on server
Copy the code

Command mode allows not only to encapsulate a request (ServerCommand) and transmit it to a receiver (Server), but also to better handle a given request. In this case, this is better handled by storing the execution history of the command. In Spring, we find the rationale for the instruction design pattern in the features of the beanFactory afterprocessor. To define them quickly, the application context launches the post-processor and can be used to perform some operations on the bean being created (I won’t go into detail here, but I’ll write an article on this later to analyze the source details).

When we translate the command logic presented in the previous Demo and compare it to the Spring Bean factory post-processor, we can distinguish between the following actors: The post-processor bean(which means implementing the BeanFactoryPostProcessor interface) is the command, Org. Springframework. Context. Support. PostProcessorRegistrationDelegate is the caller (it performs all registered post processor bean postProcessBeanFactory method, Here to see the next piece of code below) and receiver org. Springframework. Beans. Factory. Config. ConfigurableListableBeanFactory can before the element (bean) initialization structure modify them (for example: Properties can be changed before the bean is initialized.

Also, to review the Demo above, it is the same as command history management in our Demo. PostProcessorRegistrationDelegate BeanPostProcessorChecker contains an inner class, it can record when a bean is not in conformity with the processing conditions.

Can observe PostProcessorRegistrationDelegate of two pieces of code:

/ * *
	 * 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(a) < this.beanPostProcessorTargetCount) {
				if (logger.isInfoEnabled()) {
					logger.info("Bean '" + beanName + "' of type [" + bean.getClass(a) +
							"] 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(a);
			}
			return false;
		}
	}
Copy the code

Definition after the call, use is ConfigurableListableBeanFactory instance (see BeanPostProcessorChecker comments) :

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(a) + 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));
	}
Copy the code

To summarize the process, I want to get the object from the BeanFactory (i.e., to get the result of a command), so if I want to get the object and have implemented some modification of the idea, I use the post-processor, Beans that implement a post-processor interface (commands can be passed different parameters to get different results, or command scripts can be modified), and then need an executor (when we do automated operations, we don’t just run one script, PostProcessorRegistrationDelegate here is concentrated to manage these), finally the result is shown by the BeanFactory cough up.

Visitor pattern

The next behavioral design pattern to be introduced is Visitor: an abstraction that allows an object to be accessed through another type of object. In this brief definition, objects that use this design pattern are treated as visitors or objects that can be accessed. The first visitor should have accessibility support. A real-world example of this pattern could be a car quality inspector, who checks car parts such as wheels, brakes, and engines to determine whether the car is of good quality. Let’s do a JUnit test case:

public class VisitorTest {

  @Test
  public void test(a) {
    CarComponent car = new Car(a);
    Mechanic mechanic = new QualifiedMechanic(a);
    car.accept(mechanic);
    assertTrue("After qualified mechanics visit, the car should be broken".
      car.isBroken());
    Mechanic nonqualifiedMechanic = new NonQualifiedMechanic(a);
    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(a);
}

class QualifiedMechanic implements Mechanic {

  @Override
  public void visit(CarComponent element) {
    element.setBroken(true);
  }

  @Override
  public String getName(a) {
    return "qualified";
  }
}

class NonQualifiedMechanic implements Mechanic {

  @Override
  public void visit(CarComponent element) {
    element.setBroken(true);
  }

  @Override
  public String getName(a) {
    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(a) {
    return this.broken;
  }
}

class Car extends CarComponent {

  private boolean broken = false;
  private CarComponent[] components;

  public Car(a) {
    components = new CarComponent[] {
      new Wheels(), new Engine(), new Brake(a)
    };
  }

  @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(a);
        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(a) {
          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);
  }
}
Copy the code

In this example, we can see that they have two mechanisms (visitors, essentially exempt and non-exempt) : pass and fail. The visible object exposed to them is the car. Determine which role should be applied to the interviewees by their acceptance (through the code mechanic. GetName ().equals(“qualified”)). When the visitor is qualified, Car asks him to analyze all the components. If the visitor fails, Car considers its intervention useless and returns false in the isBroken() method (in effect, for an exemption). Spring implements the visitor design pattern in beans configuration. To watch, we can see the org. Springframework. Beans. Factory. Config. BeanDefinitionVisitor object, the object is used for bean metadata and its parse String (such as: XML attributes with scope or factory method names) or Object (for example, parameters in a constructor definition). The parsed value is set for judgment in the BeanDefinition instance associated with the bean being parsed. See the BeanDefinitionVisitor code snippet for the source code:

/ * *
 * 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(a);
  visitIndexedArgumentValues(cas.
    getIndexedArgumentValues());
  visitGenericArgumentValues(cas.
    getGenericArgumentValues());
}

protected void visitParentName(BeanDefinition beanDefinition) {
  String parentName = beanDefinition.getParentName(a);
  if (parentName ! = null) {
    String resolvedName = resolveStringValue(parentName);
    if (!parentName.equals(resolvedName)) {
      beanDefinition.setParentName(resolvedName);
    }
  }
}
Copy the code

In this case, they just have access and do not have any additional control over the visitors (which the car inspector did in the Demo). The access here involves parsing the parameters of the given BeanDefinition and replacing them with parsed objects.

In our last article on design patterns in Spring, we found two behavior patterns: a command pattern for post-processing bean factories and a visitor pattern for converting defined bean parameters into object-oriented (String or Object instance) parameters.

The original:Design Patterns in the Spring Framework (5)