3

1. Spring的资源抽象接口

假如有一个文件位于Web应用的类路径下,用户可以通过以下方式对这个文件资源进行访问:

  • 通过FileSystemResource以文件系统绝对路径的方式进行访问;

  • 通过ClassPathResource以类路径的方式进行访问;

  • 通过ServletContextResource以相对于Web应用根目录的方式进行访问。

2. BeanFactory的类体系结构

image

  • BeanFactory:位于类结构树的顶端,最主要的方法是getBean(String beanName),从容器中返回特定类型的bean

  • ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法

  • HierarchicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口方法访问父容器

  • ConfigurableBeanFactory:增强了IoC容器的可定制性

  • AutowireCapableBeanFactory:定义了将容器中的bean按照某种规则进行自动装配的方法

  • SingletonBeanRegistry:定义了允许在运行期向容器注册单实例bean的方法

  • 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

天歌
39 声望6 粉丝

天歌应春籥,非是为春风。