Design patterns in Spring Framework - part 4

This article is the 4th about design patterns used in Spring framework. It'll present new 3 patterns implemented in this framework.

At the begin, we'll discover 2 patterns belonging to the family of structural patterns: adapter and decorator. At the 3rd and the last part, we'll talk about creational design pattern which is singleton.

Spring design pattern - adapter

Adapter design pattern used when we need to adapt the interface to given situation without modifying its behavior. It means that we'll change used object without changing mechanism before invoking this object. To illustrate it in real world, imagine the situation when you want to make a hole with a drill. To make a small hole, you'll use small drill bit and to make a big one, big drill bit. You can see it in below code:

public class AdapterTest {

  public static void main(String[] args) {
    HoleMaker maker = new HoleMakerImpl();
    maker.makeHole(1);
    maker.makeHole(2);
    maker.makeHole(30);
    maker.makeHole(40);
  }
}

interface HoleMaker {
  public void makeHole(int diameter);
}

interface DrillBit {
  public void makeSmallHole();
  public void makeBigHole();
}

// Two adaptee objects
class BigDrillBit implements DrillBit {
        
  @Override
  public void makeSmallHole() {
    // do nothing
  }

  @Override
  public void makeBigHole() {
    System.out.println("Big hole is made byt WallBigHoleMaker");
  }
}

class SmallDrillBit implements DrillBit {
        
  @Override
  public void makeSmallHole() {
    System.out.println("Small hole is made byt WallSmallHoleMaker");
  }

  @Override
  public void makeBigHole() {
    // do nothing
  }
}

// Adapter class
class Drill implements HoleMaker {

  private DrillBit drillBit;

  public Drill(int diameter) {
    drillBit = getMakerByDiameter(diameter);
  }

  @Override
  public void makeHole(int diameter) {
    if (isSmallDiameter(diameter)) {
            drillBit.makeSmallHole();
    } else {
            drillBit.makeBigHole();
    }
  }

  private DrillBit getMakerByDiameter(int diameter) {
    if (isSmallDiameter(diameter)) {
            return new SmallDrillBit();
    }
    return new BigDrillBit();
  }

  private boolean isSmallDiameter(int diameter) {
    return diameter < 10;
  }
}

// Client class
class HoleMakerImpl implements HoleMaker {
        
  @Override
  public void makeHole(int diameter) {
    HoleMaker maker = new Drill(diameter);
    maker.makeHole(diameter);
  }
}

This code will print:

Small hole is made byt SmallDrillBit
Small hole is made byt SmallDrillBit
Big hole is made byt BigDrillBit
Big hole is made byt BigDrillBit

As you can see, the hole is made with adapted DrillBit object. If the hole's diameter is smaller than 10, we use SmallDrillBit. If it's bigger, we use BigDrillBit.

Spring uses adapter design pattern to handle load-time-weaving in different servlet containers. Load-time-weaving is used in Aspect-Oriented Programming (AOP) to inject AspectJ's aspects to byte code during class loading. Another ways to inject these aspects are compile-time injection or static injection on already compiled classes.

A good illustration of this is the case of JBoss, included in package org.springframework.instrument.classloading.jboss. We retrieve there JBossLoadTimeWeaver class responsible for weaving management for JBoss container. However, the class loader is different for JBoss 6 (uses JBossMCAdapter instance) and JBoss 7/8 (uses JBossModulesAdapter instance). Depending to JBoss version, we initialize corresponding adapter in JBossLoadTimeWeaver constructor (exactly as for Drill's constructor in our example):

public JBossLoadTimeWeaver(ClassLoader classLoader) {
  private final JBossClassLoaderAdapter adapter;

  Assert.notNull(classLoader, "ClassLoader must not be null");
  if (classLoader.getClass().getName().startsWith("org.jboss.modules")) {
    // JBoss AS 7 or WildFly 8
    this.adapter = new JBossModulesAdapter(classLoader);
  }
  else {
    // JBoss AS 6
    this.adapter = new JBossMCAdapter(classLoader);
  }
}

Further, this adapter instance is used to make weaving operations depending on running servlet container version:

@Override
public void addTransformer(ClassFileTransformer transformer) {
  this.adapter.addTransformer(transformer);
}

@Override
public ClassLoader getInstrumentableClassLoader() {
  return this.adapter.getInstrumentableClassLoader();
}

Spring design pattern - decorator

The second design pattern described here looks similar to adapter. It's decorator. The main role if this design pattern is to add supplementary role to given object. In the real world, the illustration of this pattern should be a coffee. Usually black and strong, you can add ("decorate" it with) sugar and milk to get coffee less stronger. Coffee is here decorated object and sugar with milk are the decorators. Below you can find an example showing this coffee decoration:

public class DecoratorSample {

  @Test
  public void test() {
    Coffee sugarMilkCoffee=new MilkDecorator(new SugarDecorator(new BlackCoffee()));
    assertEquals(sugarMilkCoffee.getPrice(), 6d, 0d);
  }
}

// decorated
abstract class Coffee{
  protected int candied=0;
  protected double price=2d;
  public abstract int makeMoreCandied();
  public double getPrice(){
    return this.price;
  }
  public void setPrice(double price){
    this.price+=price;
  }
}
class BlackCoffee extends Coffee{
  @Override
  public int makeMoreCandied(){
    return 0;
  }
  @Override
  public double getPrice(){
    return this.price;
  }
}

// abstract decorator
abstract class CoffeeDecorator extends Coffee{
  protected Coffee coffee;
  public CoffeeDecorator(Coffee coffee){
    this.coffee=coffee;
  }
  @Override
  public double getPrice(){
    return this.coffee.getPrice();
  }
  @Override
  public int makeMoreCandied(){
    return this.coffee.makeMoreCandied();
  }
}

// concrete decorators
class MilkDecorator extends CoffeeDecorator{
  public MilkDecorator(Coffee coffee){
    super(coffee);
  }
  @Override
  public double getPrice(){
    return super.getPrice()+1d;
  }
  @Override
  public int makeMoreCandied(){
    return super.makeMoreCandied()+1;
  }
}
class SugarDecorator extends CoffeeDecorator{
  public SugarDecorator(Coffee coffee){
    super(coffee);
  }
  @Override
  public double getPrice(){
    return super.getPrice()+3d;
  }
  @Override
  public int makeMoreCandied(){
    return super.makeMoreCandied()+1;
  }
}

This sample of decorator is based on invocations of parent methods which change the final property (price and candied level in our case). In Spring we retrieve decorator design pattern in a class handling cache synchronization with Spring-managed transactions. This class is org.springframework.cache.transaction.TransactionAwareCacheDecorator.

Which characteristics of this class prove that it's a decorator for org.springframework.cache.Cache object ? First of all, exactly as in our coffee example, the TransactionAwareCacheDecorator's constructor takes in parameter decorated object (Cache):

private final Cache targetCache;
/**
 * Create a new TransactionAwareCache for the given target Cache.
 * @param targetCache the target Cache to decorate
 */
public TransactionAwareCacheDecorator(Cache targetCache) {
  Assert.notNull(targetCache, "Target Cache must not be null");
  this.targetCache = targetCache;
}

Secondly, a new behavior is added to decorated Cache. As we can read in the comment of TransactionAwareCacheDecorator, the main purpose of it is to provide synchronization level between cache and Spring transactions. It's achieved thanks to org.springframework.transaction.support.TransactionSynchronizationManager in two of Cache methods: put and evict:

@Override
public void put(final Object key, final Object value) {
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
    TransactionSynchronizationManager.registerSynchronization(
      new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
          targetCache.put(key, value);
        }
    });
  }
  else {
    this.targetCache.put(key, value);
  }
}

@Override
public void evict(final Object key) {
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
          TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
              @Override
              public void afterCommit() {
                targetCache.evict(key);
              }
          });
  }
  else {
    this.targetCache.evict(key);
  }
}

This pattern looks similar to adapter, isn't it ? However, they're both different. As we can see, adapter adapts the object to runtime environment, ie. if we run in JBoss 6, we use different class loader than in JBoss 7. Decorator works every time with the same main object (Cache) and only adds new behaviour to it (as synchronization with Spring transactions in our example).

Spring design pattern - singleton

Now, it's the turn of the very popular design pattern, singleton. As we've already explained in the article about singleton and prototype beans in Spring Framework, singleton is considered as one from several bean scopes. This scope creates only one instance of given bean in each application context. Unlike signleton design pattern, Spring limits the number of instances to application context. Singleton's design pattern in Java applications limits the quantity of these instances to a whole space managed by given class loader. It means that you can use the same classloader for two Spring's context and retreive two singleton-scoped beans.

Before diving to Spring singletons, let's take a look on Java's singleton example:

public class SingletonTest {

  @Test
  public void test() {
    President president1 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
    President president2 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
    assertTrue("Both references of President should point to the same object", president1 == president2);
    System.out.println("president1 = "+president1+" and president2 = "+president2);
    // sample output
    // president1 = com.waitingforcode.test.President@17414c8 and president2 = com.waitingforcode.test.President@17414c8

  }

}

enum SingletonsHolder {
  
  PRESIDENT(new President());
  
  private Object holdedObject;
  
  private SingletonsHolder(Object o) {
          this.holdedObject = o;
  }
  
  public Object getHoldedObject() {
          return this.holdedObject;
  }
        
}

class President {
}

This test case proves that only one instance of President holded by SingletonsHolder exists. In Spring, we can find the concept of singletons in bean factory (for example in org.springframework.beans.factory.config.AbstractFactoryBean):

/**
 * Expose the singleton instance or create a new prototype instance.
 * @see #createInstance()
 * @see #getEarlySingletonInterfaces()
 */
@Override
public final T getObject() throws Exception {
  if (isSingleton()) {
    return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
  }
  else {
    return createInstance();
  }
}

We see that when demanded object is treated like singleton, it's initialized only once and returned after every time with the same instance of bean's class. We can see it in given example, similar to our President case seen previously. Tested bean is defined as:

<bean id="shoppingCart" class="com.waitingforcode.data.ShoppingCart" />
And the test case looks like:
public class SingletonSpringTest {

  @Test
  public void test() {
    // retreive two different contexts
    ApplicationContext firstContext = new FileSystemXmlApplicationContext("applicationContext-test.xml");
    ApplicationContext secondContext = new FileSystemXmlApplicationContext("applicationContext-test.xml");
    
    // prove that both contexts are loaded by the same class loader
    assertTrue("Class loaders for both contexts should be the same", 
      firstContext.getClassLoader() == secondContext.getClassLoader());
    // compare the objects from different contexts
    ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
    ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
    assertFalse("ShoppingCart instances got from different application context shouldn't be the same", 
      firstShoppingCart == secondShoppingCart);
    
    // compare the objects from the same context
    ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
    assertTrue("ShoppingCart instances got from the same application context should be the same", 
      firstShoppingCart == firstShoppingCartBis);
  }
}

This test case shows the main difference between singletons in Spring and purely design pattern singletons. Despite the same class loader used to load two application contexts, the instances of ShoppingCart aren't the same. But when we compare the instances retrieved twice and belonging to the same context, we perceive that they're equal.

Design patterns described in this article are used by Spring to handle bean creation. Thanks to singleton, Spring can control that only one instance of so specified bean is available in each application context. Thanks to adapter, Spring decides which layer should be used to handle load-time weaving in JBoss servlet container. The third design pattern, decorator, is used to add synchronization features to Cache objects.


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!