Design Patterns in Spring Framework (2)

In the last part, we talked about the design patterns in Spring. We talked about three object creation patterns (generator builder, factory method factory method, abstract factory abstract factory) and one behavior pattern (interpreter interpreter). In this part, we will focus on more structure and behavior patterns. .

In this part, we look at two structural patterns ( structural) and two behavioral ( behavioral) patterns respectively. The first two will be Structural Design Patterns: the Proxy Pattern ( proxy) and the Composition Pattern ( composite). The latter two examples are behavioral patterns: strategy( strategy) and template method( template method).

Spring Design Patterns - Proxy Patternproxy

Object orientation ( OOP) is probably the most popular concept in programming. However, Spring introduces another coding paradigm, Aspect Oriented Programming ( AOP). To define it simply, it AOPis a kind of programming oriented to some specific points of the system, such as exception throwing, execution of specific types of methods, and so on. AOPAllows us to perform some additional actions before and after the execution of these points in the system. How can this be done? It can be done via listener( listener). But in this approach, we have to define every possible listener invocation everywhere (eg at the beginning of the method). It is for this reason that Spring does not use this idea. Instead, Spring implements another design pattern that can accomplish this task with additional method calls, which is the proxy design pattern ( proxy).

The "proxy" works like a mirror of the object. Through the proxy mode, proxy objects can not only overwrite the original objects, but also extend their functionality. Therefore, for an object that can only print some text on the screen, we can add an object to filter the displayed text. The call to the second object can then be defined through the proxy. A proxy is an object that encapsulates the real original object. For example, if you want to call a bean Waiter, you can call the proxy object of the bean, and the proxy object behaves exactly the same as the original object.

A good example of the proxy pattern is org.springframework.aop.framework.ProxyFactoryBean. This factory constructs AOP proxies for Spring beans. The class implements the FactoryBeaninterface, and a method is defined in the interface getObject(). This method is used to return the bean instance requested from the bean factory. When this pattern is applied, the method returns not the original bean object itself, but the AOP proxy object of the bean object. Compared with the underlying bean object, the proxy object has the same behavior, but can also add some decorations, which can execute some additional methods before the methods of the proxied bean object are executed. An example of
use :ProxyFactory

public class TestProxyAop {

  @Test
  public void test() {
    // new House()是将要被代理的对象,House类实现了接口Construction
    ProxyFactory factory = new ProxyFactory(new House());
    // 告诉代理工厂代理对象和被代理对象哪些行为必须保持一致,通过接口Construction定义告知
    factory.addInterface(Construction.class);
    // 在调用被代理对象的方法之前,需要额外调用的逻辑
    factory.addAdvice(new BeforeConstructAdvice());
    factory.setExposeProxy(true);

    Construction construction = (Construction) factory.getProxy();
    construction.construct();
    assertTrue("Construction is illegal. "
      + "Supervisor didn't 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("I'm 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")) {
      // 该分支在被代理对象的 construct 方法被调用之前被调用  , 授权可以盖房
      ((Construction) target).givePermission();
    }
  }

}

The test will pass because we are not operating directly on the original House object instance but on the proxy object. Before calling Housethe target method of the original object construct, the proxy object will first call BeforeConstructAdvicethe beforemethod, in this method, it will Housedo an "authorization" on the original object (Annotation: By calling Housethe method on the original object givePermission()). Because the proxy layer can easily distribute requests to other objects, it provides additional functionality to the target object. To do this, we can just modify beforethe filter condition in the method.

Spring Design Patterns - Composition Patternscomposite

Another structural design pattern is the composition pattern composite. In the previous part, we used generators builderto construct complex objects. Another way to construct complex objects is the composition pattern composite. This pattern builds a larger object based on multiple existing objects with the same behavior. This larger object still has the same characteristics of each small object, such as having the same behavior methods.

A non-Spring example of composition mode, such as writing a piece of HTMLtext, which HTMLconsists of paragraphs, which can be HTMLtags spanor 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>");
  }

}

In this example, an PTagobject is a composite object. We can distinguish composite objects from non-composite objects because they PTagcan contain one or more non-composite objects ( PTagprivate properties in them List tags). Non-composed objects are called leaves. TextTagIt is called a component because it provides common behavior for both types of objects.

In Spring, we extract the concept of composite objects into interfaces org.springframework.beans.BeanMetadataElement, which are used to configure bean objects. It is the base interface from which all objects inherit. Now, on the one hand, we have leaves, org.springframework.beans.factory.parsing.BeanComponentDefinitionrepresented by passages, and on the other hand, we have combinations org.springframework.beans.factory.parsing.CompositeComponentDefinition. CompositeComponentDefinitionActs as a component because it contains a method addNestedComponent(ComponentDefinition component)that allows private final List nestedComponentsadding leaves to properties. As you can see from this list, the components of BeanComponentDefinitionand are .CompositeComponentDefinitionorg.springframework.beans.factory.parsing.ComponentDefinition

Spring Design Patterns - Strategy Patternstrategy

The third concept we describe in this article is the strategy pattern. A strategy defines multiple objects that accomplish the same thing in different ways. The way this is done depends on the strategy employed. As an example, we can look at the way to go abroad. It can be by bus, plane, boat or even by car. All of these methods can take us to the destination country. However, we will definitely check our bank account before deciding the most suitable method. If there is enough money, we will take the fastest way (probably a private jet). If you don't have that much money, then the slowest (bus, self-drive). Here, the bank account number acts as a defining factor for the chosen strategy.

In classes org.springframework.web.servlet.mvc.multiaction.MethodNameResolver, Spring uses the strategy pattern. The purpose of its implementation is parameterization MultiActionController. Before explaining the strategy, we need to understand the tool class MultiActionController. This class allows multiple types of requests to be handled in the same class. Like every controller in Spring, MultiActionControllerexecute methods that respond to provided requests. Determining which method will be called employs a strategy. The implementation of the corresponding decision process in MethodNameResolverthe same package ParameterMethodNameResolveris an example of this. There can be multiple criteria for deciding which method to call: attribute mapping, HTTP request parameters or URL path. An example of this strategy implementation is ParameterMethodNameResolvera method of a class getHandlerMethodName:

@Override
public 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()) {
        + "' 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;
}

From the above code we can see that the name of the target method is determined by the provided parameter mapping, pre-defined properties or the existence of parameters in the URL.

Spring Design Patterns - Template Methodstemplate method

The last design pattern we'll cover in this section is the template method. This pattern defines a skeleton of the class's behavior, and then defers the execution of some steps to subclasses. There is usually a finalmethod that acts as a synchronizer. It executes the methods defined by the subclasses in the given order. In the real world, we can compare the template approach to building a house. In the process of building a house, no matter which construction company it is, you always need to start by laying the foundation before you can do anything else. This execution logic is held by a method and we cannot modify it. The other methods, whether it is laying the foundation or painting the walls, are all template methods, which are all related to the company that actually built the house. Let's see this from the following example:

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");
  }

}

This code will output:

Constructing very strong foundations
Constructing very strong wall

In classes org.springframework.context.support.AbstractApplicationContext, Spring uses template methods. It is not a template method (like our example above), but multiple template methods. For example, return the latest version of the internal bean factory obtainFreshBeanFactoryby calling two abstract methods refreshBeanFactory(for refreshing the bean factory) and getBeanFactory(for getting the bean factory). This method, and a few others, will be used by the public methods public void refresh() throws BeansException, IllegalStateExceptionused to construct the application context object. For specific examples of the implementation of these two abstract methods, you can refer to the following in the same package GenericApplicationContext, which defines these two abstract methods as follows:

/**
  * Do nothing: We hold a single internal BeanFactory and rely on callers
  * to register beans through our public methods (or the BeanFactory's).
  * @see #registerBeanDefinition
  */
@Override
protected 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;
}

@Override
protected 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.
  */
@Override
protected final void closeBeanFactory() {
  this.beanFactory.setSerializationId(null);
}

/**
  * Return the single internal BeanFactory held by this context
  * (as ConfigurableListableBeanFactory).
  */
@Override
public 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;
}

Summarize

This time we explained how Spring uses the behavioral design pattern to better organize the context (template method template method) and how to decide which method to execute (strategy pattern strategy). There are also two structural design patterns used to simplify the AOP part (the proxy pattern proxy) and to construct complex objects (the composition pattern composite).

English original

Directory of articles in the series

Design Patterns in Spring Framework (5)
Design Patterns in Spring Framework (4)
Design Patterns in Spring Framework (3)
Design Patterns in Spring Framework (2) Design Patterns
in Spring Framework (1)

Directory of articles in the series

Design Patterns in Spring Framework (5)
Design Patterns in Spring Framework (4)
Design Patterns in Spring Framework (3)
Design Patterns in Spring Framework (2) Design Patterns
in Spring Framework (1)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325641416&siteId=291194637