spring学习总结005 --- IOC容器启动源码(事件机制)

Posted sniffs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring学习总结005 --- IOC容器启动源码(事件机制)相关的知识,希望对你有一定的参考价值。

接着AbstractApplicationContext.refresh方法:initApplicationEventMulticaster,该方法用来初始化事件广播器,流程如下:

protected void initApplicationEventMulticaster() {
    // 获取BeanFactory, 默认是DefaultListableBeanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果用户定义了name为applicationEventMulticaster的事件广播器, 直接将其作为容器的事件广播器
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 未自定义bean, 使用SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // 将默认事件广播器存放到BeanFactory的beanDefinationMap中
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No ‘" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "‘ bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

那么事件广播器有啥用呢?这要从spring提供的事件机制说起:

1、spring事件机制接口类

spring提供的事件机制是基于观察者模型或者订阅-发布模型的,spring提供了三个接口分别用于事件定义、事件发布、事件广播、事件监听:ApplicationEvent(抽象类更合理)、ApplicationEventPublisher、ApplicationEventMulticaster、ApplicationListener

(1)ApplicationEvent

技术图片

ApplicationEvent应Java事件机制要求继承了EventObject,构造方法可以指定事件源

(2)ApplicationEventPublisher

技术图片

(3)ApplicationEventMulticaster

技术图片

(4)ApplicationListener

技术图片

2、spring如何具有发送消息能力 

refresh方法中有这样一个方法:prepareBeanFactory,用来为BeanFactory做一些定制化处理,其中有一行代码为BeanFactory添加了一个BeanPostProcessor ----  ApplicationContextAwareProcessor

技术图片

该后置处理器在Bean初始化前对Aware接口类型的Bean做了一些特殊处理:将特定参数的bean传递给实现了Aware接口的bean;重点看ApplicationContextAwareProcessor中的invokeAwareInterfaces方法

技术图片

ApplicationContext作为事件发布器??看ApplicationContext继承关系,发现ApplicationContext实现了ApplicationEventPublisher接口

技术图片

3、spring事件广播能力

spring事件的发布依赖事件广播器,那么spring如何具有事件广播能力呢?

spring在用户未配置名为applicationEventMulticaster的bean的情况下,创建了一个默认的事件广播器 ---- SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster通过multicastEvent方法来广播事件

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    // 获取事件类型
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 获取线程池
    Executor executor = getTaskExecutor();
    // 通过事件及类型获取对应的事件监听器
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 异步或者同步方式广播事件
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // listener处理
        listener.onApplicationEvent(event);
    }
    catch (ClassCastException ex) {
        String msg = ex.getMessage();
        if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
            Log logger = LogFactory.getLog(getClass());
            if (logger.isTraceEnabled()) {
                logger.trace("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}

那么事件发布器怎么利用事件广播器来广播事件呢?答案在AbstractApplicationContext的publishEvent方法。至此清楚了事件完整的发布机制

技术图片

4、事件接收能力

ApplicationListener的实现类用来接收事件,spring框架怎么具备接收事件的能力呢?答案依旧在refresh方法调用的prepareBeanFactory方法中,该方法添加了一个BeanPostProcessor ----  ApplicationListenerDetector

技术图片

在Bean初始化之后,会调用ApplicationListenerDetector的postProcessAfterInitialization方法

技术图片

然后判断ApplicationContext中事件广播器是否为空,将事件监听器保存起来

技术图片

5、实战示例

public class UserApplicationEvent extends ApplicationEvent {

    public UserApplicationEvent(User user) {
        super(user);
    }
}
@Slf4j
@Component
public class UserApplicationEventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void saveUser(User user) {
        log.info("start publish event.....");
        applicationEventPublisher.publishEvent(new UserApplicationEvent(user));
    }
}
@Slf4j
@Component
public class UserEventListener implements ApplicationListener<UserApplicationEvent> {
    @Override
    public void onApplicationEvent(UserApplicationEvent event) {
        log.info("Recv event:{}", event.getSource());
    }
}
public class Test11 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean10.xml");
        UserApplicationEventPublisher publisher = context.getBean(UserApplicationEventPublisher.class);
        publisher.saveUser(User.builder()
                .id(1)
                .name("bale")
                .info(Info.builder().age(28).build())
                .build());
    }
}

执行结果:

技术图片

以上是关于spring学习总结005 --- IOC容器启动源码(事件机制)的主要内容,如果未能解决你的问题,请参考以下文章

MyEclipse Spring 学习总结一 Spring IOC容器

Spring IOC 学习总结

spring学习总结007 --- IOC容器级生命周期接口

Spring源码分析总结-IOC容器初始化

spring技术内幕读书笔记之IoC容器的学习

Spring IOC容器生命周期阶段总结