zoukankan      html  css  js  c++  java
  • 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());
        }
    }

    执行结果:

  • 相关阅读:
    python基础#1
    shell脚本基础练习题
    shell计算100以内加法
    shell脚本添加用户
    python学习ing
    框架
    前端
    python基础之数据类型-面向对象
    python四种列表的插入方法及其效率
    Charles高阶操作
  • 原文地址:https://www.cnblogs.com/sniffs/p/13259860.html
Copyright © 2011-2022 走看看