原創: 瑞查德-Jackhtml
在以前的兩篇文章中,咱們看到了一些在Spring框架中實現的設計模式。這一次咱們會發現這個流行框架使用的3種新模式。java
本文將從描述兩個創意設計模式開始:原型和對象池。spring
最後咱們將重點關注行爲模式—>觀察者。數據庫
本篇前傳:設計模式
這篇文章的第一個設計模式是原型。能夠經過官方文檔查找有關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 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中使用一種特定的原型設計模式,它將初始化 bean原型做用域
。新對象基於配置文件中的bean定義。咱們能夠看到,在給定的例子中:ide
<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定義建立的。最初,cart1
和 cart2
對象的 id
值爲 9
.它在測試結束時被修改,以證實兩個引用都屬於兩個不一樣的對象。post
Spring
中使用的另外一個模型是對象池設計模式。其主要目的在於在一個池中保存特定數量的對象,並根據須要從新使用。經過它,咱們能夠改善咱們想要使用 巨型對象
的響應時間。 巨型
意味着這些對象的構造須要不少時間(例如:持有數據庫鏈接的對象),最好重用已經存在的和未獲取的對象,而不是建立新對象。
Spring還使用線程池來管理其調度部分。一些示例位於org.springframework.scheduling.concurrent中。咱們檢索數據庫( SpringJDBC
)項目中的對象池的想法。數據庫鏈接池不是由 Spring
直接實現的,而是適用於 Spring
工做方式的項目,如 C3P0
或 JakartaCommonsDBCP
鏈接池。
這裏呈現的最後一個設計模式是觀察者。當一個或幾個課程正在等待具體事件時可使用它。觀察者模式由一個科目和觀察員名單組成。一個很好的例子就是 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; 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(); } }
能夠看到,關於咱們的 Button
實例點擊的事件被髮送到全部的觀察者對象。從這些對象開始下載頁面內容,第二個將在事件的信息保存在註冊表中。在 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 {//新版本這裏直接咔嚓掉,上面的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 SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { private Executor taskExecutor; private ErrorHandler errorHandler; public SimpleApplicationEventMulticaster() { } public SimpleApplicationEventMulticaster(BeanFactory beanFactory) { this.setBeanFactory(beanFactory); } public void setTaskExecutor(Executor taskExecutor) { this.taskExecutor = taskExecutor; } protected Executor getTaskExecutor() { return this.taskExecutor; } public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } protected ErrorHandler getErrorHandler() { return this.errorHandler; } public void multicastEvent(ApplicationEvent event) { this.multicastEvent(event, this.resolveDefaultEventType(event)); } //發佈事件:經過池執行任務的方式來作併發處理,這樣就把以前的對象池模式給利用上了 public void multicastEvent(final ApplicationEvent 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的原型
,避免從新建立巨型對象的對象池,以及將應用程序的上下文事件分派給適當的監聽器的觀察者。