Design patterns in Spring Framework - part 3

on waitingforcode.com

Design patterns in Spring Framework - part 3

In two previous articles we saw some of design patterns implemented in Spring framework. This time we'll discover 3 new patterns used by this popular framework.

This article will start by describing two creational design patterns: prototype and object pool. At the end we'll focus on behavioral pattern, observer.

Spring design pattern - prototype

The first design pattern from this article is prototype. Similar name is described in the article about bean scopes in Spring. Prototype design pattern looks similarly to the scope with the same name. This design pattern allows to create the instance of one object by copying already existing object. The copy should be true copy. It means that all attributes of new object should be the same as the attributes of copied object. If it's not clear, anything better than a simple JUnit case to illustrate that:

public class PrototypeTest {

  @Test
  public void test() {
    Robot firstRobot = new Robot("Droid#1");
    Robot secondRobot = (Robot) firstRobot.clone();
    assertTrue("Cloned robot's instance can't be the same as the" 
      +" source robot instance", 
      firstRobot != secondRobot);
    assertTrue("Cloned robot's name should be '"+firstRobot.getName()+"'" 
      +" but was '"+secondRobot.getName()+"'", 
      secondRobot.getName().equals(firstRobot.getName()));
  }

}


class Robot implements Cloneable {
  private String name;
  
  public Robot(String name) {
    this.name = name;
  }
  
  public String getName() {
    return this.name;
  }

  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

In Spring, a kind of specific prototype design pattern is used in org.springframework.beans.factory.support.AbstractBeanFactory which initializes the beans prototype-scoped. New object is based on bean definition from configuration file. We can see that in given example:


  

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"applicationContext-test.xml"})
public class SpringPrototypeTest {

  @Autowired
  private BeanFactory beanFactory;
  
  @Test
  public void test() {
    ShoppingCart cart1 = (ShoppingCart) beanFactory.getBean("shoppingCart");
    assertTrue("Id of cart1 should be 9 but was "+cart1.getId(), 
      cart1.getId() == 9);
    cart1.setId(100);
    ShoppingCart cart2 = (ShoppingCart) beanFactory.getBean("shoppingCart");
    assertTrue("Id of cart2 should be 9 but was "+cart2.getId(), 
      cart2.getId() == 9);
    assertTrue("Id of second cart ("+cart2.getId()+") shouldn't be the same as the first one: "+cart1.getId(), 
      cart1.getId() != cart2.getId());
    cart2.setId(cart1.getId());
    assertTrue("Now (after cart2.setId(cart1.getId())), the id of second cart ("+cart2.getId()+") should be the same as the first one: "
      +cart1.getId(), cart1.getId() == cart2.getId());
    assertTrue("Both instance shouldn't be the same", cart1 != cart2);
  }

}

As we can see in previous example, ShoppingCart instances are created directly from bean definition. Initially the value of id for both, cart1 and cart2 objects, is 9. It's modified at the end of the test to prove that both references belong to two different objects.

Spring design pattern - object pool

Another model used in Spring is object pool design pattern. The main purpose of it consists on holding a specific number of objects in one pool and reuse it on demand. Thanks to it we can improve the response time when we want to use greedy objects. Greedy means here that the construction of these objects takes lot of time (for example: objects holding database connection) and it's better to reuse already existing and not acquired objects rather than create the new ones.

Spring also uses thread pools to manage its scheduling part. Some of examples are located in org.springframework.scheduling.concurrent. We retrieve the idea of object pooling in database (Spring JDBC) project. The database connections pools aren't implemented directly by Spring but by projects which are adapted to Spring way of working, as C3P0 or Jakarta Commons DBCP connection pool.

Spring design pattern - observer

The last design pattern presented here is observer. It's used when one or several classes are waiting for concrete event. Observer is composed by one subject and a list of observers. A good example of it are GUI interfaces where click on a button (button is a subject) provokes some actions started by listeners (observers), as for example: opening of new page. You can see it in action in following example:

public class ObserverTest {

  @Test
  public void test() {
    Observer pageOpener = new PageOpener();
    Observer register = new Register();
    Button btn = new Button();
    btn.addListener(pageOpener);
    btn.addListener(register);
    btn.clickOn();
    assertTrue("Button should be clicked but it wasn't", 
      btn.wasClicked());
    assertTrue("Page opener should be informed about click but it wasn't", 
      pageOpener.wasInformed());
    assertTrue("Register should be informed about click but it wasn't", 
      register.wasInformed());
  }

}

class Button {
        
  private boolean clicked;
  private List listeners;
  
  public List getListeners() {
    if (this.listeners == null) {
      this.listeners = new ArrayList();
    }
    return this.listeners;
  }
  
  public void addListener(Observer observer) {
    getListeners().add(observer);
  }
  
  public boolean wasClicked() {
    return this.clicked;
  }
  
  public void clickOn() {
    this.clicked = true;
    informAll();
  }
  
  private void informAll() {
    for (Observer observer : getListeners()) {
      observer.informAboutEvent();
    }
  }
        
}

abstract class Observer {
  protected boolean informed;
  
  public void informAboutEvent() {
    this.informed = true;
  }
  
  public boolean wasInformed() {
    return this.informed;
  }
}

class PageOpener extends Observer {
        
  @Override
  public void informAboutEvent() {
    System.out.println("Preparing download of new page");
    super.informAboutEvent();
  }
        
}

class Register extends Observer {

  @Override
  public void informAboutEvent() {
    System.out.println("Adding the action to register");
    super.informAboutEvent();
  }
}

As you can see, the event about the click on our Button's instance is transmitted to all observer objects. From these objects, one starts to download page content and the second saves the information about the event in registry. In Spring, observer design pattern is used to transmit events associated with application context to the implementations of org.springframework.context.ApplicationListener. To see how they're implemented, let's take a look on AbstractApplicationContext class:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
  implements ConfigurableApplicationContext, DisposableBean {
  /** Statically specified listeners */
  private Set> applicationListeners = new LinkedHashSet>();
  
  // some other fields and methods
  @Override
  public void addApplicationListener(ApplicationListener listener) {
    if (this.applicationEventMulticaster != null) {
      this.applicationEventMulticaster.addApplicationListener(listener);
    }
    else {
      this.applicationListeners.add(listener);
    }
  }

  /**
    * Return the list of statically specified ApplicationListeners.
    */
  public Collection> getApplicationListeners() {
    return this.applicationListeners;
  }

  /**
    * Add beans that implement ApplicationListener as listeners.
    * Doesn't affect other listeners, which can be added without being beans.
    */
  protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String lisName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(lisName);
    }
  }
}

In presented code, the listeners are added internally into application context class and after, in the registerListeners() method, they are registered to appropriate event multicaster represented by interface org.springframework.context.event.ApplicationEventMulticaster. Event multicasters are responsible for managing of different listeners and publishing events to them.

This time we discovered 3 design patterns: prototype used to create beans under the same called scope, object pool which avoids to recreate greedy objects and observer which dispatches application's context events to appropriate listeners.

Share on: