ApplicationContext作為資源載入器;ApplicationContext作為事件發佈者; Java原生提供了事件發佈機制 EventObject對象作為發佈的事件,EventListener作為處理髮布事件的監聽器。但是其並沒有提供發佈者的角色來橋接EventObject和Event ...
ApplicationContext作為資源載入器;ApplicationContext作為事件發佈者;
Java原生提供了事件發佈機制------EventObject對象作為發佈的事件,EventListener作為處理髮布事件的監聽器。但是其並沒有提供發佈者的角色來橋接EventObject和EventListener。Spring對java原生的事件發佈機製做了擴展:一方面擴展了EventObject和EventListener,使其可以記錄事件發佈時間,擴展了事件發佈介面;更重要的一點,ApplicationContext自身可以充當事件發佈者(因為其實現了ApplicationEventPublisher介面),完成了本應該由開發者來實現的代碼(如果使用java原生髮布事件機制的話)。當對象A(被觀察者)發生變化時,有一個發佈者(可以是被觀察者自身,也可以委托第三方如spring容器)發出通知,事物B(觀察者)能夠收到通知更新自己的狀態。這個就是經常使用到的觀察者模式,spring容器提供了這種觀察者模式的支持。我們通過一個例子來說明如何在Spring框架中使用事件發佈,並能讓觀察者(listener)得到消息。首先,自定義我們的EventObject,作為發佈者和監聽者之間約定好的事件對象。
public class MyEvent extends ApplicationEvent { /** * */ private static final long serialVersionUID = 1L; /** * @param source */ public MyEvent(Object source){ super(source); // TODO Auto-generated constructor stub } }
自定義的MyEvent繼承了ApplicationEvent,ApplicationEvent類是spring框架繼承EventObject而來,加入了獲取發佈時間的方法。其次,自定義我們的EventListener,它負責監聽容器發佈的ApplicationEvent事件併進行處理:
public class MyListener implements ApplicationListener { /* (non-Javadoc) * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) */ @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof MyEvent) { System.out.println(event == null ? "" : event.getSource()); } } }
因為我們只關心MyEvent事件的發佈,所以在onApplicationEvent方法中進行了判斷,過濾其他不相關發佈事件。定義完了事件和監聽器,基本就剩下發佈事件的代碼了。發佈者由ApplicationContext來充當:
public class TestPublisher { public static void main ( String args[] ) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); // applicationContext.xml配置文件中已經配置好了監聽器 ctx.publishEvent(new MyEvent("Hello World")); // 發佈事件,監聽器接收到事件消息 } }
最後,不要忘記在配置文件中添加監聽對象:
<bean class="com.alibaba.china.publisher.MyListener" />
spring容器會自動載入配置文件中所有的ApplicationListener對象,把它註冊到觀察者列表中。當有事件發佈的時候,就從這個觀察者列表中挨個通知事件發佈。程式執行結果如下:
其他細節點:
- ApplicationContext自動載入所有的Bean ApplicationContext自動載入所有的Bean;
- ApplicationContext自動識別BeanFactoryPostProcessor,BeanPostProcessor,並註冊到容器中。
ApplicationContext自動載入所有的Bean:ApplicationContext相對於BeanFactory不同的一點是,BeanFactory在用到Bean的時候才會去載入bean(在beanFactory.getBean(“beanName”)之前不會載入beanName的對象)。而ApplicationContext不同,在容器創建時就已經把所有的Bean載入進容器了。
ApplicationContext自動識別BeanFactoryPostProcessor,BeanPostProcessor,並註冊到容器中:BeanFactoryPostProcessor是容器在載入完BeanDefinition之後,容器初始化之前對beanFactory做處理的一個擴展機制。比如我們的antx配置項替換類PropertyPlaceholderConfigurer。我們只需要在配置文件中聲明PropertyPlaceholderConfigurer這類BeanFactoryPostProcessor對象,ApplicationContext就能夠識別出來並自動將這些processor註冊到容器中。BeanPostProcessor也是同樣的道理,ApplicationContext能夠自動識別xml中配置好的這類bean併進行容器的註冊。BeanFactory就不能這麼簡潔了,必須我們手動去把那些BeanFactoryPostProcessor以及BeanPostProcessor對象註冊到容器中。ApplicationContext能夠自己感知到這些processor,我們的工作只是去實現自定義的(或者直接使用spring已經實現了的)BeanFactoryPostProcessor和BeanPostProcessor,併在配置文件中聲明。是不是覺得很神奇?ApplicationContext能做到感知的原因就在於其對mdb(Merged BeanDefinition)的處理。
org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)實現: // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // beanFactory的getBeanNamesForType方法能夠根據對象的類型(此處是BeanFactoryPostProcessor.class),從mdb中找到他們的beanName String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); ...... // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. Collections.sort(priorityOrderedPostProcessors, new OrderComparator()); // 這裡所有聲明在配置文件中的BeanFactoryPostProcessor都被調用,執行介面的postProcessBeanFactory方法。 invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors);
上面的代碼中會從mdb中找出BeanFactoryPostProcessor介面的對象,並對它們進行排序,然後根據這些BeanFactoryPostProcessor依次對beanFactory處理。