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
相关文章
相关标签/搜索