Design patterns in Spring Framework - part 1

Design patterns help to follow good practices of programming. Spring framework, as one of the most popular web frameworks, also uses some of them.

Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I'm currently writing one on that topic and the first chapters are already available in πŸ‘‰ Early Release on the O'Reilly platform

I also help solve your data engineering problems πŸ‘‰ contact@waitingforcode.com πŸ“©

This article will show how which design patterns are used in Spring Framework. It's the first part of 5 articles dedicated to this subject. This time we'll discover 4 design patterns used in Spring framework: interpreted, builder, factory method and abstract factory. Each part will, first, explain the principles of given pattern. At the second time, a example of Spring use will be done.

Spring design pattern - interpreter

In real world, we, humans, need to interpret gestures. They can have, regarding to the culture, a different signification. It's our interpretation which gives them a meaning. In programming we need also to analyze one thing and decide what does it mean. We can do it with interpreted design pattern.

This pattern is based on expression and evaluator parts. The first one represents a thing to analyze. This analyze is made by evaluator which know the meaning of characters composing the expression. The operation of undesrtanding is made inside a context.

Spring uses this pattern mainly in Spring Expression Language (SpEL). To remind quickly, SpEL is a language those expressions are analyzed and executed by Spring's org.springframework.expression.ExpressionParser implementations. These implementations take SpEL expressions given as Strings and translate them into instances of org.springframework.expression.Expression. The context component is represented by org.springframework.expression.EvaluationContext implementations, as for example: StandardEvaluationContext.

An example of SpEL can look like that:

Writer writer = new Writer();
writer.setName("Writer's name");
StandardEvaluationContext modifierContext = new StandardEvaluationContext(subscriberContext);
modifierContext.setVariable("name", "Overriden writer's name");
parser.parseExpression("name = #name").getValue(modifierContext);
System.out.println("writer's name is : " + writer.getName());

The output should print "Overriden writer's name". As you can see, the property of one object was modified through an expression "name = #name" which is understandable only by ExpressionParser thanks to provided context (modifierContext instance in previous sample).

Spring design pattern - builder

Builder design pattern is the first pattern belonging to the family of creational patterns. This pattern is used to simplify the construction of complex objects. To understand the concept, imagine an object illustrating programmer's CV. In this object we want to store the personal information (first name, address etc.) as well as technical information (knowing languages, realized projects etc.). The construction of this object could look like:

// with constructor
Programmer programmer = new Programmer("first name", "last name", "address Street 39", "ZIP code", "City", "Country", birthDateObject, new String[] {"Java", "PHP", "Perl", "SQL"}, new String[] {"CRM system", "CMS system for government"});
// or with setters
Programmer programmer = new Programmer();
programmer.setName("first name");
programmer.setLastName("last name");
// ... multiple lines after
programmer.setProjects(new String[] {"CRM system", "CMS system for government"});

Builder allows us to decompose clearly the object construction by using internal builder object that passes the values to a parent class. In the case of our programmer, a builder could look like:

public class BuilderTest {

  @Test
  public void test() {
    Programmer programmer = new Programmer.ProgrammerBuilder().setFirstName("F").setLastName("L")
            .setCity("City").setZipCode("0000A").setAddress("Street 39")
            .setLanguages(new String[] {"bash", "Perl"}).setProjects(new String[] {"Linux kernel"}).build();
    assertTrue("Programmer should be 'F L' but was '"+ programmer+"'", programmer.toString().equals("F L"));
  }

}

class Programmer {
  private String firstName;
  private String lastName;
  private String address;
  private String zipCode;
  private String city;
  private String[] languages;
  private String[] projects;
  
  private Programmer(String fName, String lName, String addr, String zip, String city, String[] langs, String[] projects) {
    this.firstName = fName;
    this.lastName = lName;
    this.address = addr;
    this.zipCode = zip;
    this.city = city;
    this.languages = langs;
    this.projects = projects;
  }
  
  public static class ProgrammerBuilder {
    private String firstName;
    private String lastName;
    private String address;
    private String zipCode;
    private String city;
    private String[] languages;
    private String[] projects;
    
    public ProgrammerBuilder setFirstName(String firstName) {
      this.firstName = firstName;
      return this;
    }
    
    public ProgrammerBuilder setLastName(String lastName) {
      this.lastName = lastName;
      return this;
    }
    
    public ProgrammerBuilder setAddress(String address) {
      this.address = address;
      return this;
    }
    
    public ProgrammerBuilder setZipCode(String zipCode) {
      this.zipCode = zipCode;
      return this;
    }
    
    public ProgrammerBuilder setCity(String city) {
      this.city = city;
      return this;
    }
    
    public ProgrammerBuilder setLanguages(String[] languages) {
      this.languages = languages;
      return this;
    }
    public ProgrammerBuilder setProjects(String[] projects) {
      this.projects = projects;
      return this;
    }
    
    public Programmer build() {
      return new Programmer(firstName, lastName, address, zipCode, city, languages, projects);
    } 
  }
  
  @Override
  public String toString() {
    return this.firstName + " "+this.lastName;
  }
  
}

As you can see, the complexity of object construction is hidden behind a builder, an internal static class that accepts the chained method invocation. In Spring, we can retrieve this logic in org.springframework.beans.factory.support.BeanDefinitionBuilder class. It's a class that allows us to define a bean programatically. As we could see it in article about bean factory post processors, BeanDefinitionBuilder contains several methods which set the values for associated implementation of AbstractBeanDefinition abstract class, as scope, factory method, properties etc. To see how does it work, take a look on these methods:

public class BeanDefinitionBuilder {
	   /**
    * The {@code BeanDefinition} instance we are creating.
    */
  private AbstractBeanDefinition beanDefinition;

  // ... some not important methods for this article

  // Some of building methods
  /**
    * Set the name of the parent definition of this bean definition.
    */
  public BeanDefinitionBuilder setParentName(String parentName) {
    this.beanDefinition.setParentName(parentName);
    return this;
  }

  /**
    * Set the name of the factory method to use for this definition.
    */
  public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {
    this.beanDefinition.setFactoryMethodName(factoryMethod);
    return this;
  }

  /**
    * Add an indexed constructor arg value. The current index is tracked internally
    * and all additions are at the present point.
    * @deprecated since Spring 2.5, in favor of {@link #addConstructorArgValue}
    */
  @Deprecated
  public BeanDefinitionBuilder addConstructorArg(Object value) {
    return addConstructorArgValue(value);
  }

  /**
    * Add an indexed constructor arg value. The current index is tracked internally
    * and all additions are at the present point.
    */
  public BeanDefinitionBuilder addConstructorArgValue(Object value) {
    this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
                    this.constructorArgIndex++, value);
    return this;
  }

  /**
    * Add a reference to a named bean as a constructor arg.
    * @see #addConstructorArgValue(Object)
    */
  public BeanDefinitionBuilder addConstructorArgReference(String beanName) {
    this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
                    this.constructorArgIndex++, new RuntimeBeanReference(beanName));
    return this;
  }

  /**
    * Add the supplied property value under the given name.
    */
  public BeanDefinitionBuilder addPropertyValue(String name, Object value) {
    this.beanDefinition.getPropertyValues().add(name, value);
    return this;
  }

  /**
    * Add a reference to the specified bean name under the property specified.
    * @param name the name of the property to add the reference to
    * @param beanName the name of the bean being referenced
    */
  public BeanDefinitionBuilder addPropertyReference(String name, String beanName) {
    this.beanDefinition.getPropertyValues().add(name, new RuntimeBeanReference(beanName));
    return this;
  }

  /**
    * Set the init method for this definition.
    */
  public BeanDefinitionBuilder setInitMethodName(String methodName) {
    this.beanDefinition.setInitMethodName(methodName);
    return this;
  }

  // Methods that can be used to construct BeanDefinition
  /**
    * Return the current BeanDefinition object in its raw (unvalidated) form.
    * @see #getBeanDefinition()
    */
  public AbstractBeanDefinition getRawBeanDefinition() {
    return this.beanDefinition;
  }

  /**
    * Validate and return the created BeanDefinition object.
    */
  public AbstractBeanDefinition getBeanDefinition() {
    this.beanDefinition.validate();
    return this.beanDefinition;
  }
}

Spring design pattern - factory method

The second member of creational family is factory method design pattern. It's completely adapted to working with dynamic environments as Spring framework. In fact, this pattern allows the initialization of object through a public static method, called factory method. In this conception, we need to define an interface to create the object. But the creation is made by the classes using the concerned object.

But before jumping to Spring world, let's make a sample with Java code:

public class FactoryMethodTest {

  @Test
  public void test() {
    Meal fruit = Meal.valueOf("banana");
    Meal vegetable = Meal.valueOf("carrot");
    assertTrue("Banana should be a fruit but is "+fruit.getType(), fruit.getType().equals("fruit"));
    assertTrue("Carrot should be a vegetable but is "+vegetable.getType(), vegetable.getType().equals("vegetable"));
  }

}

class Meal {
        
  private String type;

  public Meal(String type) {
    this.type = type;
  }

  public String getType() {
    return this.type;
  }

  // Example of factory method - different object is created depending on current context
  public static Meal valueOf(String ingredient) {
    if (ingredient.equals("banana")) {
      return new Meal("fruit");
    }
    return new Meal("vegetable");
  }
}

In Spring we can create beans thanks to specified factory method. This method acts exactly as valueOf method seen in previous code sample. It's static and can take no or multiple parameters. To better understand the case, let's take a look on real example. The configuration first:


    



	

And now the bean concerned by this initialization:

public class Welcomer {
  private String message;
  
  public Welcomer(String message) {
    this.message = message;
  }

  public static Welcomer createWelcomer(MessageLocator messagesLocator) {
    Calendar cal = Calendar.getInstance();
    String msgKey = "welcome.pm";
    if (cal.get(Calendar.AM_PM) == Calendar.AM) {
      msgKey = "welcome.am";
    }
    return new Welcomer(messagesLocator.getMessageByKey(msgKey));
  }
}

When Spring will construct welcomerBean, it won't make it through classical constructor, but through defined static factory method, createWelcomer. Note also that this method accepts some of parameters (instance of MessageLocator bean holding all available messages) thanks to tag, usually reserved to classic constructors.

Spring design pattern - abstract factory

The last one, abstract factory design pattern, looks similar to factory method. The difference is that we can consider abstract factory as a factory in industrial meaning of this word, ie. as something that provides needed objects. Components of factory are: abstract factory, abstract product, product and client. To be more precise, abstract factory defines the way of constructing objects. Abstract product is a result of this construction. Product is a concrete result of the same construction. Client is somebody who demands the creation of products to abstract factory.

Once again, before going into Spring details, we'll start by illustrating this concept through sample Java code:

public class FactoryTest {

  // Test method which is the client
  @Test
  public void test() {
    Kitchen factory = new KitchenFactory();
    KitchenMeal meal = factory.getMeal("P.1");
    KitchenMeal dessert = factory.getDessert("I.1");
    assertTrue("Meal's name should be 'protein meal' and was '"+meal.getName()+"'", meal.getName().equals("protein meal"));
    assertTrue("Dessert's name should be 'ice-cream' and was '"+dessert.getName()+"'", dessert.getName().equals("ice-cream"));
  }

}

// abstract factory
abstract class Kitchen {
  public abstract KitchenMeal getMeal(String preferency);
  public abstract KitchenMeal getDessert(String preferency);
}

// concrete factory
class KitchenFactory extends Kitchen {
  @Override
  public KitchenMeal getMeal(String preferency) {
    if (preferency.equals("F.1")) {
      return new FastFoodMeal();
    } else if (preferency.equals("P.1")) {
      return new ProteinMeal();
    }
    return new VegetarianMeal();
  }

  @Override
  public KitchenMeal getDessert(String preferency) {
    if (preferency.equals("I.1")) {
      return new IceCreamMeal();
    }
    return null;
  }
}

// abstract product
abstract class KitchenMeal {
  public abstract String getName();
}

// concrete products
class ProteinMeal extends KitchenMeal {
  @Override
  public String getName() {
    return "protein meal";
  }
}

class VegetarianMeal extends KitchenMeal {
  @Override
  public String getName() {
    return "vegetarian meal";
  }
}

class FastFoodMeal extends KitchenMeal {
  @Override
  public String getName() {
    return "fast-food meal";
  }
}

class IceCreamMeal extends KitchenMeal {
  @Override
  public String getName() {
    return "ice-cream";
  }
}

As we can see in this example, abstract factory encapsulates the creation of objects. The objects creation can be make as well with factory method pattern as with classic constructor use. In Spring, the example of factory is org.springframework.beans.factory.BeanFactory. Through its implementations we can access beans from Spring's container. Depending of adopted strategy, getBean method can return already created object (shared instance, singleton scope) or initialize new one (prototype scope). Among the implementations of BeanFactory we can distinguish: ClassPathXmlApplicationContext, XmlWebApplicationContext, StaticWebApplicationContext, StaticPortletApplicationContext, GenericApplicationContext, StaticApplicationContext. Below you can find one example of use of factory in Spring web applications:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:test-context.xml"})
public class TestProduct {
        
  @Autowired
  private BeanFactory factory;
  
  @Test
  public void test() {
    System.out.println("Concrete factory is: "+factory.getClass());
    assertTrue("Factory can't be null", factory != null);
    ShoppingCart cart = (ShoppingCart) factory.getBean("shoppingCart");
    assertTrue("Shopping cart object can't be null", cart != null);
    System.out.println("Found shopping cart bean:"+cart.getClass());
  }
}

In this case, the abstract factory is represented by BeanFactory interface. The concrete factory is printed in the first System.out and is the instance of org.springframework.beans.factory.support.DefaultListableBeanFactory. The abstract product of it is an Object. Concrete product, in our case, is abstract product (Object) casted to ShoppingCart instance.

This first article introduces to extremely interesting world of correctly organized programming thanks to design patterns. Here, we can see the use of interpreter, builder, factory method and factory in Spring framework. The first one is there to helps to interpret texts expressed in SpEL. The three last patterns belong to the family of creational design patterns and theirs main purpose in Spring consists on simplify the creation of objects. They do that by decomposing the initialization of complex objects (builder) or by centralizing the initialization at the commun point.


If you liked it, you should read:

πŸ“š Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!