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容器