Spring框架中的设计模式(三)

前两篇文章我们看到了Spring框架实现的一些模式,这次我们再讲解这个流行框架使用的另外三个模式。

这篇文章会从两个对象创建模式开始:原型模式(prototype)和对象池(object pool)。最后我们会聚焦在一个行为模式 : 观察者(observer)。

Spring 设计模式 - 原型模式 prototype

本文首先谈原型模式(prototype)。在Spring关于bean作用域的文章中,Spring也用了类似的名字”原型”(prototype)用于描述一种bean的作用域。原型设计模式看起来和同名的作用域接近。这个模式用于从已经存在的对象复制创建新的对象实例。这个复制要是一个真正的复制。意思是所有新对象的属性都必须和来源对象的一模一样(译注:但不是同一个对象)。如果不清楚这个概念,用一个JUnit例子做个演示:

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

Spring中,org.springframework.beans.factory.support.AbstractBeanFactory中使用了这样一种特定的原型设计模式来初始化作用域为原型(prototype)的bean。新的对象基于配置文件中的bean定义创建出来。看下面的例子:

首先是配置文件

<!--注意下面bean定义中作用域指定为原型 prototype-->
<bean id="shoppingCart" class="com.waitingforcode.data.ShoppingCart" scope="prototype">
  <property name="id" value="9">
</property></bean>

然后是测试用例

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

}

从上面的例子可以看到,ShoppingCart实例直接从bean定义中创建。所有两个ShoppingCart实例,cart1cart2,创建后属性id初始值都是9。测试方法中cart1的属性id修改成了和cart2的属性id不同,最后两个对象的id属性又设置为了相同。这个测试证明了两个ShoppingCart实例指向两个不同的对象。

Spring设计模式-对象池object pool

Spring中使用的另外一种设计模式是对象池。其主要目的是维持一个包含特定数量对象的池子,然后按需重用池中这些对象。通过该机制,我们可以在使用”贪心对象”(greedy object)时提升响应时间。这里”贪心“指的是这些对象的构建需要很长的时间(比如:数据库连接池持有对象),所以最好重用这些已经存在的对象而不是创建新对象。

Spring也使用了线程池管理它的调度部分。org.springframework.scheduling.concurrent包里面有一些例子。Spring将该思路抽取到了数据库项目Spring JDBC中。数据库连接池并不直接由Spring实现,而是采用了一些可以适配到Spring工作的项目,比如C3P0或者Jakarta Commons DBCP连接池。

Spring设计模式-观察者observer

本文要讲的最后一个设计模式是观察者模式observer。当一个或者多个类在等待一个具体的事件时会使用到该设计模式。观察者模式由一个主题对象(被观察者)和一系列观察者对象组成。一个很好的例子是GUI界面上的一个按钮点击(按钮就是一个被观察的主题对象)会引起监听者(观察者)启动一些动作。比如打开一个新的页面。你可以从下面的例子中看它是怎么工作的:

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<observer> listeners;

  public List<observer> getListeners() {
    if (this.listeners == null) {
      this.listeners = new ArrayList<observer>();
    }
    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();
  }
}

正如你看到的,我们按钮实例上的点击传播到了观察者对象中。这些对象中,一个开始下载页面内容,另外一个将事件信息保存到注册表。Spring中,观察者设计模式用于传播应用上下文关联事件到org.springframework.context.ApplicationListener接口的实现。想知道它是怎么工作的,看看类AbstractApplicationContext:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
  implements ConfigurableApplicationContext, DisposableBean {
  /** Statically specified listeners */
  private Set<applicationlistener<?>> applicationListeners = 
      new LinkedHashSet<applicationlistener<?>>();

  // 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<applicationlistener<?>> 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);
    }
  }
}

在这段代码中,监听器从内部被添加到了应用上下文,并且后来在registerListeners方法里,监听器被注册到相应的事件多播器,由org.springframework.context.event.ApplicationEventMulticaster代表。事件多播器负责管理不同的监听器和向他们传播事件。

总结

这部分我们讲了三种设计模式 : 原型模式(prototype),用于创建同名作用域的bean;对象池(object pool),避免重复创建贪心对象;观察者(observer),将应用上下文事件分发到正确的监听器。

英文原文

该系列文章目录

Spring框架中的设计模式(五)
Spring框架中的设计模式(四)
Spring框架中的设计模式(三)
Spring框架中的设计模式(二)
Spring框架中的设计模式(一)

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/79778899
今日推荐