在之前的两篇文章中,我们看到了一些在Spring框架中实现的设计模式。这一次我们会发现这个流行框架使用的3种新模式。
本文将从描述两个创意设计模式开始:原型和对象池。最后我们将重点关注行为模式—>观察者。
这篇文章的第一个设计模式是 原型
。可以通过官方文档查找有关Spring作用域中的bean作用域的文章中介绍了类似的概念( prototype
)。原型设计模式与有用相同名称的( prototype
)作用域有点相似。此设计模式允许通过复制已存在的对象来创建一个对象的实例。副本应该是 真正的副本
。这意味着新对象的所有属性应与复制对象的属性相同。如果不清楚,比一个简单的 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 Robotimplements Cloneable{ private String name; public Robot(String name){ this.name = name; } publicStringgetName(){ return this.name; } protectedObjectclone()throwsCloneNotSupportedException{ return super.clone(); } }
在 Spring
中,在 org.springframework.beans.factory.support.AbstractBeanFactory
中使用一种特定的原型设计模式,它将初始化 bean原型作用域
。新对象基于配置文件中的bean定义。我们可以看到,在给定的例子中:
<beanid="shoppingCart"class="com.waitingforcode.data.ShoppingCart"scope="prototype"> <propertyname="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定义创建的。最初, cart1
和 cart2
对象的 id
值为 9
.它在测试结束时被修改,以证明两个引用都属于两个不同的对象。
Spring
中使用的另一个模型是 对象池设计模式
。其主要目的在于在一个池中保存特定数量的对象,并根据需要重新使用。通过它,我们可以改善我们想要使用 巨型对象
的响应时间。 巨型
意味着这些对象的构造需要很多时间(例如:持有数据库连接的对象),最好重用已经存在的和未获取的对象,而不是创建新对象。
Spring还使用线程池来管理其调度部分。一些示例位于 org.springframework.scheduling.concurrent中
。我们检索数据库( Spring JDBC
)项目中的对象池的想法。数据库连接池不是由 Spring
直接实现的,而是适用于 Spring
工作方式的项目,如 C3P0
或 Jakarta Commons DBCP
连接池。
这里呈现的最后一个设计模式是 观察者
。当一个或几个课程正在等待具体事件时可以使用它。观察者模式由一个科目和观察员名单组成。一个很好的例子就是 GUI界面
,其中点击按钮(按钮是主题)会引起听众(观察者)启动的一些操作(再说的直白点就是电影院一场电影这个 subject
,需要 观众
(也就是观察者咯),电影产生的一些画面产生的事件,比如恐怖 电影给男人女人带来的不同的感官的感受,传播到观察者也就是观众的眼里所带来的不一样的反应,这个中间一般会添加一个 事件传播者
,在后面解释 Spring
的例子的时候会说到),例如:打开一个新页面这个动作。可以参考下面的例子:
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; publicList<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 PageOpenerextends Observer{ @Override public void informAboutEvent(){ System.out.println("Preparing download of new page"); super.informAboutEvent(); } } class Registerextends Observer{ @Override public void informAboutEvent(){ System.out.println("Adding the action to register"); super.informAboutEvent(); } }
可以看到,关于我们的 Button
实例点击的事件被发送到所有的观察者对象。从这些对象开始下载页面内容,第二个将在事件的信息保存在注册表中。在 Spring
中,观察者设计模式用于将与应用程序上下文相关的事件传输到 org.springframework.context.ApplicationListener的实现
。要了解它们的实现方法,我们来看一下 AbstractApplicationContext
类(老版本的代码,新版本的请自行对照):
public abstract class AbstractApplicationContextextends 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 {//新版本这里直接咔嚓掉,上面的applicationEventMulticaster一旦为空,就会报错的 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
表示的适当的事件多路广播器(因为有很多listeners)。 EventMulticaster
负责管理不同的 listener
和向他们发布事件。
public class SimpleApplicationEventMulticasterextends AbstractApplicationEventMulticaster{ private Executor taskExecutor; private ErrorHandler errorHandler; public SimpleApplicationEventMulticaster(){ } public SimpleApplicationEventMulticaster(BeanFactory beanFactory){ this.setBeanFactory(beanFactory); } public void setTaskExecutor(Executor taskExecutor){ this.taskExecutor = taskExecutor; } protectedExecutorgetTaskExecutor(){ return this.taskExecutor; } public void setErrorHandler(ErrorHandler errorHandler){ this.errorHandler = errorHandler; } protectedErrorHandlergetErrorHandler(){ return this.errorHandler; } public void multicastEvent(ApplicationEvent event){ this.multicastEvent(event, this.resolveDefaultEventType(event)); } //发布事件:通过池执行任务的方式来做并发处理,这样就把之前的对象池模式给利用上了 public void multicastEvent(finalApplicationEvent event, ResolvableType eventType){ ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event); Iterator var4 = this.getApplicationListeners(event, type).iterator(); while(var4.hasNext()) { final ApplicationListener<?> listener = (ApplicationListener)var4.next(); Executor executor = this.getTaskExecutor(); if(executor != null) { executor.execute(new Runnable() { public void run(){ SimpleApplicationEventMulticaster.this.invokeListener(listener, event); } }); } else { this.invokeListener(listener, event); } } } ... }
这次我们讲3种设计模式:用于在同一个调用作用域内创建 bean的原型
,避免重新创建巨型对象的对象池,以及将应用程序的上下文事件分派给适当的监听器的观察者。
有更多疑问请加qq群523409180 讨论的