Learn Spring - Spring IoC

1. Spring的資源抽象接口

假若有一個文件位於Web應用的類路徑下,用戶能夠經過如下方式對這個文件資源進行訪問:java

  • 經過FileSystemResource以文件系統絕對路徑的方式進行訪問;web

  • 經過ClassPathResource以類路徑的方式進行訪問;spring

  • 經過ServletContextResource以相對於Web應用根目錄的方式進行訪問。緩存

2. BeanFactory的類體系結構

image

  • BeanFactory:位於類結構樹的頂端,最主要的方法是getBean(String beanName),從容器中返回特定類型的beanapp

  • ListableBeanFactory:該接口定義了訪問容器中Bean基本信息的若干方法異步

  • HierarchicalBeanFactory:父子級聯IoC容器的接口,子容器能夠經過接口方法訪問父容器編輯器

  • ConfigurableBeanFactory:加強了IoC容器的可定製性ide

  • AutowireCapableBeanFactory:定義了將容器中的bean按照某種規則進行自動裝配的方法函數

  • SingletonBeanRegistry:定義了容許在運行期向容器註冊單實例bean的方法post

  • BeanDefinitionRegistry:每個bean在容器中經過BeanDefinition對象表示,BeanDefinitionRegistry定義了向容器手工註冊bean的方法

  • Spring在DefaultSingletonBeanRegistry類中提供了一個用於緩存單實例bean的緩存器,以HashMap實現,單實例的bean以beanName爲key保存在這個HashMap

3. ApplicationContext的類體系結構

image

  • ApplicationEventPublisher:讓容器擁有發佈應用上下文事件的功能,包括容器啓動事件、關閉事件等。實現了ApplicationListener事件監聽接口的Bean 能夠接收到容器事件,並對事件進行響應處理。在ApplicationContext抽象實現類AbstractApplicationContext中,咱們能夠發現存在一個ApplicationEventMulticaster,它負責保存全部監聽器,以便在容器產生上下文事件時通知這些事件監聽者。

  • MessageSource:爲應用提供i18n國際化消息訪問的功能;

  • ResourcePatternResolver:全部ApplicationContext實現類都實現了相似於PathMatchingResourcePatternResolver的功能,能夠經過帶前綴的Ant風格的資源文件路徑裝載Spring的配置文件。

  • LifeCycle:該接口是Spring 2.0加入的,該接口提供了start()stop()兩個方法,主要用於控制異步處理過程。在具體使用時,該接口同時被ApplicationContext實現及具體Bean實現,ApplicationContext會將start/stop的信息傳遞給容器中全部實現了該接口的Bean,以達到管理和控制JMX、任務調度等目的。

  • ConfigurableApplicationContext擴展於ApplicationContext,它新增長了兩個主要的方法:refresh()close(),讓ApplicationContext具備啓動、刷新和關閉應用上下文的能力。在應用上下文關閉的狀況下調用refresh()便可啓動應用上下文,在已經啓動的狀態下,調用refresh()則清除緩存並從新裝載配置信息,而調用close()則可關閉應用上下文。

4. WebApplicantContext體系結構

  • 它容許從相對於Web根目錄的路徑中加載配置文件完成初始化工做。從WebApplicationContext中能夠獲取ServletContext引用,整個Web應用上下文對象將做爲屬性放置在ServletContext中,以便Web應用環境能夠訪問spring上下文。

  • WebApplicationContext擴展了ApplicationContextWebApplicationContext定義了一個常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啓動時,咱們能夠直接經過下面的語句從web容器中獲取WebApplicationContext:

WebApplicationContext wac=(WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

5. BeanFactory中Bean的生命週期

5.1 Bean生命週期

  1. 若是容器註冊InstantiationAwareBeanPostProcessor接口,調用postProcessBeforeInstantiation方法

  2. Bean的實例化(調用默認構造器)

  3. 若是容器註冊InstantiationAwareBeanPostProcessor接口,調用postProcessAfterInstantiation方法

  4. 若是容器註冊InstantiationAwareBeanPostProcessor接口,調用postProcessPropertyValues方法

  5. 根據配置設置屬性值

  6. 若是Bean實現了BeanNameAware接口,調用BeanNameAware接口的setBeanName方法

  7. 若是Bean實現了BeanFactoryAware接口,調用BeanFactoryAware接口的setBeanFactory方法

  8. 若是容器註冊了BeanPostProcessor接口,調用BeanPostProcessor接口的postProcessBeforeInitialization方法

  9. 若是Bean實現了InitializingBean接口,調用InitializingBean接口的afterPropertiesSet方法

  10. 經過init-method屬性配置的初始方法

  11. 若是容器註冊了BeanPostProcessor接口,調用BeanPostProcessor接口的postProcessAfterInitialization方法

  12. 若是是單例模式,將Bean放入緩存池中;容器銷燬時,調用DisposableBean的destroy方法;最後調用destroy-method方法

  13. 若是是多例模式,將Bean交給調用者。

5.2 初始化過程當中的方法分類

  • bean自身的方法:如調用bean構造函數實例化bean,調用Setter設置bean的屬性值,以及經過<bean>init-methoddestory-method所指定的方法;

  • bean級生命週期接口方法:如BeanNameAwareBeanFactoryAwareInitializingBeanDisposableBean,這些接口方法由bean類直接實現;

  • 容器級生命週期接口方法:如InstantiationAwareBeanPostProcessorBeanPostProcessor這兩個接口實現,通常稱它們的實現類爲「後處理器」。

5.3 說明

  • Spring的AOP等功能即經過BeanPostProcessor實現

  • 若是<bean>經過init-method屬性定義了初始化方法,將執行這個方法

  • 若是bean的做用範圍爲scope="prototype",將bean返回給調用者以後,調用者負責bean的後續生命的管理,Spring再也不管理這個bean的生命週期;若是scope="singleton",則將bean放入到Spring IoC容器的緩存池中,並將bean的引用返回給調用者,Spring繼續管理這些bean的後續生命週期

  • 對於單例的bean,當容器關閉時,將觸發Spring對bean的後續生命週期的管理工做。若是bean實現了DisposableBean接口,則將調用接口的destroy()方法

  • 對於單例的bean,若是經過destroy-method指定了bean的銷燬方法,Spring將執行這個方法

  • 後處理器的實際調用順序和註冊順序無關,在具備多個後處理器的狀況下,必須經過實現org.springframework.core.Ordered接口肯定調用順序

5.4 測試

  • applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="car" class="com.data.Car"
        p:color="color"
        init-method="init"
        destroy-method="destroy2" />
</beans>
  • Car

package com.data;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Car implements BeanNameAware, BeanFactoryAware,
    InitializingBean, DisposableBean {

    public Car() {
        System.out.println("construct car");
    }

    private String name;

    private String color;

    private BeanFactory beanFactory;

    private String beanName;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
        System.out.println("set color=" + color);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("after properties set method");
    }

    public void init() {
        System.out.println("init method");
    }

    @Override
    public void destroy() {
        System.out.println("destroy method");
    }

    public void destroy2() {
        System.out.println("my destroy method");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("set bean factory");
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("set bean name");
        this.beanName = name;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public String getBeanName() {
        return beanName;
    }
}
  • MyBeanPostProcessor

package com.beanfactory;
import java.beans.PropertyDescriptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;

public class MyBeanPostProcessor
    extends InstantiationAwareBeanPostProcessorAdapter{

    public MyBeanPostProcessor() {
        System.out.println("construct MyBeanPostProcessor");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("post process before instantiation");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("post process after instantiation");
        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(
    PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("post process property values");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post process before initialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post process after initialization");
        return bean;
    }
}
  • TestBeanFactory

package config;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.beanfactory.MyBeanPostProcessor;
import com.data.Car;

@SuppressWarnings("deprecation")
public class TestBeanFactory {

    public static void main(String[] args) {
        Resource res = new ClassPathResource(
                "/applicationContext.xml");
        XmlBeanFactory bf = new XmlBeanFactory(res);
        bf.addBeanPostProcessor(new MyBeanPostProcessor());
        System.out.println("bean factory initialization done");
        Car car1 = bf.getBean("car", Car.class);
        Car car2 = bf.getBean("car", Car.class);
        System.out.println("(car1 == car2) = " + (car1 == car2));
        System.out.println("get color=" + car1.getColor());
        bf.destroySingletons();
    }
}
  • 結果

二月 09, 2017 10:58:59 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
construct MyBeanPostProcessor
bean factory initialization done
post process before instantiation
construct car
post process after instantiation
post process property values
set color=color
set bean name
set bean factory
post process before initialization
after properties set method
init method
post process after initialization
(car1 == car2) = true
get color=color
destroy method
my destroy method

6. ApplicationContext中的Bean生命週期

6.1 流程圖

image

6.2 說明

  • 若是bean實現了org.springframework.context.ApplicationContextAware接口,會增長一個調用該接口方法setApplicationContext()的步驟

  • 若是配置文件中聲明瞭工廠後處理器接口BeanFactoryPostProcessor的實現類,則應用上下文在加載配置文件以後、初始化bean實例以前將調用這些BeanFactoryPostProcessor對配置信息進行加工處理

  • ApplicationContextBeanFactory的不一樣之處在於:前者會利用Java反射機制自動識別出配置文件中定義的BeanPostProcessorInstantiationAwareBeanPostProcessorBeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;然後者須要在代碼中經過手工調用addBeanPostProcessor()方法進行註冊

  • 對bean的初始化,BeanFactory發生在第一次調用bean時,而ApplicationContext發生在初始化容器時

6.3 測試

  • MyBeanPostProcessor同上

  • Car增長對ApplicationContextAware接口的實現,並添加@PostConstruct@PreDestroy的註解方法

package com.data;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Car implements BeanNameAware, BeanFactoryAware,
    InitializingBean, DisposableBean, ApplicationContextAware {

    public Car() {
        System.out.println("construct car");
    }

    private String name;

    private String color;

    private BeanFactory beanFactory;

    private String beanName;

    private ApplicationContext ctx;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("after properties set method");
    }

    public void init() {
        System.out.println("init method");
    }

    @Override
    public void destroy() {
        System.out.println("destroy method");
    }

    public void destroy2() {
        System.out.println("my destroy method");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("set bean factory");
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("set bean name");
        this.beanName = name;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public String getBeanName() {
        return beanName;
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("post construct");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("pre destroy");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        System.out.println("set application context");
        this.ctx = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return ctx;
    }

}
  • MyBeanFactoryPostProcessor

package com.beanfactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        System.out.println("construct MyBeanFactoryPostProcessor");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("post process bean factory");
    }
}
  • 基於Java類的Spring配置:AnnotationBeans

package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.beanfactory.MyBeanFactoryPostProcessor;
import com.beanfactory.MyBeanPostProcessor;
import com.data.Car;

@Configuration
public class AnnotationBeans {

    @Bean(name = "car", initMethod = "init", destroyMethod = "destroy2")
    public Car getCar() {
        Car car = new Car();
        car.setColor("color");
        return car;
    }

    @Bean(name = "myBeanPostProcessor")
    public MyBeanPostProcessor getMyBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }

    @Bean(name = "myBeanFactoryPostProcessor")
    public MyBeanFactoryPostProcessor getMyBeanFactoryPostProcessor() {
        return new MyBeanFactoryPostProcessor();
    }
}
  • TestApplicationContext

package config;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.data.Car;

public class TestApplicationContext {

    public static void main(String[] args) {
        /*對於以xml形式初始化的ctx,也能夠用ClassPathXmlApplicationContext
        或者FileSystemXmlApplicationContext*/
        AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext(
                        AnnotationBeans.class);
        System.out.println("application context done");
        Car car = ctx.getBean("car", Car.class);
        System.out.println("get color=" + car.getColor());
        ctx.close();
    }
}
  • 結果

二月 09, 2017 11:55:25 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@306a30c7: startup date [Thu Feb 09 23:55:25 CST 2017]; root of context hierarchy
二月 09, 2017 11:55:25 下午 org.springframework.context.annotation.ConfigurationClassEnhancer intercept
警告: @Bean method AnnotationBeans.getMyBeanFactoryPostProcessor is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
construct MyBeanFactoryPostProcessor
post process bean factory
construct MyBeanPostProcessor
post process before instantiation
post process after instantiation
post process property values
post process before initialization
post process after initialization
post process before instantiation
post process after instantiation
post process property values
post process before initialization
post process after initialization
post process before instantiation
construct car
post process after instantiation
post process property values
set bean name
set bean factory
set application context
post process before initialization
post construct
after properties set method
init method
post process after initialization
application context done
get color=color
二月 09, 2017 11:55:25 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@306a30c7: startup date [Thu Feb 09 23:55:25 CST 2017]; root of context hierarchy
pre destroy
destroy method
my destroy method

7. 容器內部工做機制

7.1 啓動源碼

Spring的AbstractApplicationContext的refresh()方法定義了Spring容器在加載配置文件後的各項處理工做

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

7.2 容器啓動流程

ContextLoaderListener經過調用繼承自ContextLoaderinitWebApplicationContext方法實例化SpringIoC容器。在實例化Spring IoC容器的過程當中,最主要的兩個方法是createWebApplicationContextconfigureAndRefreshWebApplicationContext方法。createWebApplicationContext方法用於返回XmlWebApplicationContext實例,即Web環境下的SpringIoC容器。configureAndRefreshWebApplicationContext用於配XmlWebApplicationContext,讀取web.xml中經過contextConfigLocation標籤指定的XML文件,經過調用refresh來調用AbstractApplicationContext中的refresh初始化。

  1. BeanFactory實例化XML文件中配置的bean,Spring將配置文件的bean的信息解析成爲一個個的BeanDefinition對象並裝入到容器的Bean定義註冊表,但此時Bean還未初始化;obtainFreshBeanFactory()會調用自身的refreshBeanFactory(),而refreshBeanFactory()方法由子類AbstractRefreshableApplicationContext實現,該方法返回了一個建立的DefaultListableBeanFactory對象,這個對象就是由ApplicationContext管理的BeanFactory容器對象;

  2. 調用工廠後處理器:根據反射機制從BeanDefinitionRegistry中找出全部BeanFactoryPostProcessor類型的Bean,並調用其postProcessBeanFactory()接口方法。通過第一步加載配置文件,已經把配置文件中定義的全部bean裝載到BeanDefinitionRegistry這個Beanfactory中,對於ApplicationContext應用來講這個BeanDefinitionRegistry類型的BeanFactory就是Spring默認的DefaultListableBeanFactory

  3. 註冊Bean後處理器:根據反射機制從BeanDefinitionRegistry中找出全部BeanPostProcessor類型的Bean,並將它們註冊到容器Bean後處理器的註冊表中;

  4. 初始化消息源:初始化容器的國際化信息資源;

  5. 初始化應用上下文事件廣播器;

  6. 初始化其餘特殊的Bean;

  7. 註冊事件監聽器;

  8. 初始化singleton的Bean:實例化全部singleton的Bean,並將它們放入Spring容器的緩存中;

  9. 發佈上下文刷新事件:在此處時容器已經啓動完成,發佈容器refresh事件建立上下文刷新事件,事件廣播器負責將些事件廣播到每一個註冊的事件監聽器中。

7.3 Bean加載流程

  1. ResourceLoader從存儲介質中加載Spring配置文件,並使用Resource表示這個配置文件的資源;

  2. BeanDefinitionReader讀取Resource所指向的配置文件資源,而後解析配置文件。配置文件中每個<bean>解析成一個BeanDefinition對象,並保存到BeanDefinitionRegistry中;

  3. 容器掃描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射機制自動識別出Bean工廠後處理器(實現BeanFactoryPostProcessor接口)的Bean,而後調用這些Bean工廠後處理器對BeanDefinitionRegistry中的BeanDefinition進行加工處理。主要完成如下兩項工做:

    • 對使用到佔位符的<bean>元素標籤進行解析,獲得最終的配置值,這意味對一些半成品式的BeanDefinition對象進行加工處理並獲得成品的BeanDefinition對象;

    • BeanDefinitionRegistry中的BeanDefinition進行掃描,經過Java反射機制找出全部屬性編輯器的Bean(實現java.beans.PropertyEditor接口的Bean),並自動將它們註冊到Spring容器的屬性編輯器註冊表中(PropertyEditorRegistry);

  4. Spring容器從BeanDefinitionRegistry中取出加工後的BeanDefinition,並調用InstantiationStrategy着手進行Bean實例化的工做;

  5. 在實例化Bean時,Spring容器使用BeanWrapper對Bean進行封裝,BeanWrapper提供了不少以Java反射機制操做Bean的方法,它將結合該Bean的BeanDefinition以及容器中屬性編輯器,完成Bean屬性的設置工做;

  6. 利用容器中註冊的Bean後處理器(實現BeanPostProcessor接口的Bean)對已經完成屬性設置工做的Bean進行後續加工,直接裝配出一個準備就緒的Bean。

8. Spring事件

Spring事件體系包括三個組件:事件,事件監聽器,事件廣播器。
image

  • 事件:ApplicationEvent

  • 事件監聽器:ApplicationListener,對監聽到的事件進行處理。

  • 事件廣播器:ApplicationEventMulticaster,將Spring publish的事件廣播給全部的監聽器。Spring在ApplicationContext接口的抽象實現類AbstractApplicationContext中完成了事件體系的搭建。

  • AbstractApplicationContext擁有一個applicationEventMulticaster成員變量,applicationEventMulticaster提供了容器監聽器的註冊表。

8.1 事件廣播器的初始化

private void initApplicationEventMulticaster() throws BeansException {  
    if (containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME )) {  
        this.applicationEventMulticaster = (ApplicationEventMulticaster)  
            getBean( APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class );  
        if (logger.isInfoEnabled()) {
            logger.info("Using ApplicationEventMulticaster [" + this. applicationEventMulticaster + "]" );
        }  
    }  
    else {  
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();  
        if (logger.isInfoEnabled()) {
            logger.info("Unable to locate ApplicationEventMulticaster with name '"+  APPLICATION_EVENT_MULTICASTER_BEAN_NAME +  
                        "': using default [" + this .applicationEventMulticaster + "]");  
        }  
    }  
 }

用戶能夠在配置文件中爲容器定義一個自定義的事件廣播器,只要實現ApplicationEventMulticaster就能夠了,Spring會經過反射的機制將其註冊成容器的事件廣播器,若是沒有找到配置的外部事件廣播器,Spring自動使用 SimpleApplicationEventMulticaster做爲事件廣播器。

8.2 註冊事件監聽器

private void registerListeners () throws BeansException {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    Collection listeners = getBeansOfType(ApplicationListener.class,true,false).values();
    for (Iterator it = listeners.iterator(); it.hasNext();) {
        addListener((ApplicationListener) it.next());
    }
}
protected void addListener(ApplicationListener listener) {
    getApplicationEventMulticaster().addApplicationListener(listener);
}

Spring根據反射機制,使用ListableBeanFactorygetBeansOfType方法,從BeanDefinitionRegistry中找出全部實現 org.springframework.context.ApplicationListener的Bean,將它們註冊爲容器的事件監聽器,實際的操做就是將其添加到事件廣播器所提供的監聽器註冊表中。

8.3 發佈事件

public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isDebugEnabled()) {
        logger.debug("Publishing event in context ["
                      + getDisplayName() + "]: " + event);
    }
    getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}

AbstractApplicationContextpublishEvent方法中, Spring委託ApplicationEventMulticaster將事件通知給全部的事件監聽器

8.4 Spring默認的事件廣播器SimpleApplicationEventMulticaster

public void multicastEvent( final ApplicationEvent event) {
    for (Iterator it = getApplicationListeners().iterator(); it.hasNext();) {
        final ApplicationListener listener = (ApplicationListener) it.next();
        getTaskExecutor().execute(new Runnable() {
            public void run() {
                listener.onApplicationEvent(event);
            }
        });
    }
 }

遍歷註冊的每一個監聽器,並啓動來調用每一個監聽器的onApplicationEvent方法。
因爲SimpleApplicationEventMulticastertaskExecutor的實現類是SyncTaskExecutor,所以,事件監聽器對事件的處理,是同步進行的。

8.5 舉例

  • springEvent.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.event" />
</beans>
  • MockEvent

package com.event;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;

public class MockEvent extends ApplicationContextEvent {

    public MockEvent(ApplicationContext source) {
        super(source);
    }
}
  • MockEventListener

package com.event;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MockEventListener implements ApplicationListener<MockEvent> {

    public void onApplicationEvent(MockEvent event) {
        System.out.println("mock event received");
    }
}
  • MockEventPublisher

package com.event;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;;

@Component
public class MockEventPublisher implements ApplicationContextAware {

    private ApplicationContext ctx;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }

    public void publishEvent() {
        System.out.println("publish event");
        MockEvent event = new MockEvent(this.ctx);
        ctx.publishEvent(event);
    }
}
  • MockEventTest

package com.event;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MockEventTest {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
                "/springEvent.xml");
        MockEventPublisher publisher = ctx.getBean(MockEventPublisher.class);
        publisher.publishEvent();
        ctx.close();
    }
}
  • 結果

二月 09, 2017 9:57:43 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@69d0a921: startup date [Thu Feb 09 21:57:43 CST 2017]; root of context hierarchy
二月 09, 2017 9:57:43 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [springEvent.xml]
publish event
mock event received
二月 09, 2017 9:57:44 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@69d0a921: startup date [Thu Feb 09 21:57:43 CST 2017]; root of context hierarchy
相關文章
相關標籤/搜索