前两篇文章我们看到了Spring框架实现的一些模式,这次我们再讲解这个流行框架使用的另外三个模式。
这篇文章会从两个对象创建模式开始:原型模式(prototype
)和对象池(object pool
)。最后我们会聚焦在一个行为模式 : 观察者(observer
)。
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 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
实例,cart1
和cart2
,创建后属性id
初始值都是9。测试方法中cart1
的属性id
修改成了和cart2
的属性id
不同,最后两个对象的id
属性又设置为了相同。这个测试证明了两个ShoppingCart
实例指向两个不同的对象。
object pool
Spring中使用的另外一种设计模式是对象池。其主要目的是维持一个包含特定数量对象的池子,然后按需重用池中这些对象。通过该机制,我们可以在使用"贪心对象"(greedy object)时提升响应时间。这里”贪心“指的是这些对象的构建需要很长的时间(比如:数据库连接池持有对象),所以最好重用这些已经存在的对象而不是创建新对象。
Spring也使用了线程池管理它的调度部分。org.springframework.scheduling.concurrent
包里面有一些例子。Spring将该思路抽取到了数据库项目Spring JDBC中。数据库连接池并不直接由Spring实现,而是采用了一些可以适配到Spring工作的项目,比如C3P0或者Jakarta Commons DBCP连接池。
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框架中的设计模式(一)