Springboot 学习之 事件机制

Spring事件机制是 观察者模式 的一种实现,但是除了 发布者监听者 两个角色之外,还有一个 广播者

一、成员

  1. ApplicationEvent:应用事件
  2. ApplicationEventPublisher:应用事件发布者
  3. ApplicationEventMulticaster:应用事件广播者
  4. ApplicationListener 继承 EventListener:应用事件监听者

二、关系图

在这里插入图片描述

  1. ApplicationEventPublisher:发布 ApplicationEventApplicationEventMulticaster
  2. ApplicationEventMulticaster:广播将 ApplicationEvent 发送到对应的 ApplicationListener

三、示例

情景:用户注册,注册成功会发送邮件、短信

  • 注册事件
package com.zxguan.springsecurityoauth2.config;

import com.zxguan.springsecurityoauth2.model.User;
import org.springframework.context.ApplicationEvent;

public class RegistryEvent extends ApplicationEvent {

    public RegistryEvent(User user) {
        super(user);
    }
}
  • 发布注册事件
package com.zxguan.springsecurityoauth2.service;

import com.zxguan.springsecurityoauth2.config.RegistryEvent;
import com.zxguan.springsecurityoauth2.model.User;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

@Service
public class UserRegistryService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

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

    public boolean registry(User user) {
        ApplicationEvent event = new RegistryEvent(user);
        applicationEventPublisher.publishEvent(event);
        return true;
    }
}
  • 注册事件监听
package com.zxguan.springsecurityoauth2.config;

import com.zxguan.springsecurityoauth2.model.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class RegistryEventListener implements ApplicationListener<RegistryEvent> {
    @Override
    public void onApplicationEvent(RegistryEvent event) {
        User user = (User) event.getSource();
        //发送邮件

        //发送短信
    }
}
  • 业务代码
package com.zxguan.springsecurityoauth2.controller;

import com.zxguan.springsecurityoauth2.model.User;
import com.zxguan.springsecurityoauth2.service.UserRegistryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private UserRegistryService userRegistryService;

    @RequestMapping(value = "/test")
    public Object test() {
        userRegistryService.registry(new User("zhangsan", "北京"));
        return "SUCCESS";
    }
}

四、Springboot 启动事件机制(SpringApplicationEvent 实现类)

事件类型
ApplicationStartedEvent / ApplicationStartingEvent获取 SpringApplicationRunListener 实例之后调用(ApplicationListener 注册之后)
ApplicationEnvironmentPreparedEventEnviroment 准备完毕之后,ApplicationContext 创建之前调用
ApplicationPreparedEventApplicationContext 已准备齐全,但未刷新时发布的事件调用
ApplicationReadyEvent所有都准备完成
ApplicationFailedEvent启动异常时执行事件

核心类:EventPublishingRunListenerSimpleApplicationEventMulticaster
核心方法:multicastEvent

	public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	
		......
		......
		......
	
		private final SimpleApplicationEventMulticaster initialMulticaster;
	
		public EventPublishingRunListener(SpringApplication application, String[] args) {
			this.application = application;
			this.args = args;
			this.initialMulticaster = new SimpleApplicationEventMulticaster();
			for (ApplicationListener<?> listener : application.getListeners()) {
				this.initialMulticaster.addApplicationListener(listener);
			}
		}
	
		......
		......
		......
	
		@Override
		@SuppressWarnings("deprecation")
		public void starting() {
			this.initialMulticaster
					.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
		}
	
		@Override
		public void environmentPrepared(ConfigurableEnvironment environment) {
			this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
					this.application, this.args, environment));
		}
	
		@Override
		public void contextPrepared(ConfigurableApplicationContext context) {
		
		}
	
		@Override
		public void contextLoaded(ConfigurableApplicationContext context) {
			......
			......
			......
			this.initialMulticaster.multicastEvent(
							new ApplicationPreparedEvent(this.application, this.args, context));
		}
	
		@Override
		public void finished(ConfigurableApplicationContext context, Throwable exception) {
			......
			......
			......
			this.initialMulticaster.multicastEvent(event);
		}
		......
		......
		......
	}

五、SpringMVC 事件机制(ApplicationContextEvent 实现类)

事件类型
ContextStartedEventApplicationContext 启动 时引发的事件
ContextRefreshedEventApplicationContext 初始化刷新 时引发的事件
ContextStoppedEventApplicationContext 停止 时引发的事件
ContextClosedEventApplicationContext 关闭 时引发的事件

核心类:AbstractApplicationContextSimpleApplicationEventMulticaster
核心方法:publishEvent

	public abstract class AbstractApplicationContext extends DefaultResourceLoader
			implements ConfigurableApplicationContext, DisposableBean {
	
		......
		......
		......
		/**
		 * Name of the ApplicationEventMulticaster bean in the factory.
		 * If none is supplied, a default SimpleApplicationEventMulticaster is used.
		 * @see org.springframework.context.event.ApplicationEventMulticaster
		 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
		 */
		public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
	
	
		static {
			// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
			// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
			ContextClosedEvent.class.getName();
		}
	
		......
		......
		......
		
	
		/** Helper class used in event publishing */
		private ApplicationEventMulticaster applicationEventMulticaster;
	
		......
		......
		......
	
		/**
		 * Publish the given event to all listeners.
		 * <p>Note: Listeners get initialized after the MessageSource, to be able
		 * to access it within listener implementations. Thus, MessageSource
		 * implementations cannot publish events.
		 * @param event the event to publish (may be application-specific or a
		 * standard framework event)
		 */
		@Override
		public void publishEvent(ApplicationEvent event) {
			publishEvent(event, null);
		}
	
		/**
		 * Publish the given event to all listeners.
		 * <p>Note: Listeners get initialized after the MessageSource, to be able
		 * to access it within listener implementations. Thus, MessageSource
		 * implementations cannot publish events.
		 * @param event the event to publish (may be an {@link ApplicationEvent}
		 * or a payload object to be turned into a {@link PayloadApplicationEvent})
		 */
		@Override
		public void publishEvent(Object event) {
			publishEvent(event, null);
		}
	
		/**
		 * Publish the given event to all listeners.
		 * @param event the event to publish (may be an {@link ApplicationEvent}
		 * or a payload object to be turned into a {@link PayloadApplicationEvent})
		 * @param eventType the resolved event type, if known
		 * @since 4.2
		 */
		protected void publishEvent(Object event, ResolvableType eventType) {
			Assert.notNull(event, "Event must not be null");
			if (logger.isTraceEnabled()) {
				logger.trace("Publishing event in " + getDisplayName() + ": " + event);
			}
	
			// Decorate event as an ApplicationEvent if necessary
			ApplicationEvent applicationEvent;
			if (event instanceof ApplicationEvent) {
				applicationEvent = (ApplicationEvent) event;
			}
			else {
				applicationEvent = new PayloadApplicationEvent<Object>(this, event);
				if (eventType == null) {
					eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
				}
			}
	
			// Multicast right now if possible - or lazily once the multicaster is initialized
			if (this.earlyApplicationEvents != null) {
				this.earlyApplicationEvents.add(applicationEvent);
			}
			else {
				getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
			}
	
			// Publish event via parent context as well...
			if (this.parent != null) {
				if (this.parent instanceof AbstractApplicationContext) {
					((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
				}
				else {
					this.parent.publishEvent(event);
				}
			}
		}
	
		/**
		 * Return the internal ApplicationEventMulticaster used by the context.
		 * @return the internal ApplicationEventMulticaster (never {@code null})
		 * @throws IllegalStateException if the context has not been initialized yet
		 */
		ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
			if (this.applicationEventMulticaster == null) {
				throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
						"call 'refresh' before multicasting events via the context: " + this);
			}
			return this.applicationEventMulticaster;
		}
	
		......
		......
		......
	
		@Override
		public void addApplicationListener(ApplicationListener<?> listener) {
			Assert.notNull(listener, "ApplicationListener must not be null");
			if (this.applicationEventMulticaster != null) {
				this.applicationEventMulticaster.addApplicationListener(listener);
			}
			else {
				this.applicationListeners.add(listener);
			}
		}
	
		......
		......
		......
	
		@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();
				}
			}
		}
		
		......
		......
		......
		
		/**
		 * 初始化 ApplicationEventMulticaster.
		 * 未定义时, 使用 SimpleApplicationEventMulticaster
		 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
		 */
		protected void initApplicationEventMulticaster() {
			ConfigurableListableBeanFactory beanFactory = getBeanFactory();
			if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
				this.applicationEventMulticaster =
						beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
				if (logger.isDebugEnabled()) {
					logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
				}
			}
			else {
				this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
				beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
				if (logger.isDebugEnabled()) {
					logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
							APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
							"': using default [" + this.applicationEventMulticaster + "]");
				}
			}
		}
	
		......
		......
		......
	
		/**
		 * Template method which can be overridden to add context-specific refresh work.
		 * Called on initialization of special beans, before instantiation of singletons.
		 * <p>This implementation is empty.
		 * @throws BeansException in case of errors
		 * @see #refresh()
		 */
		protected void onRefresh() throws BeansException {
			// For subclasses: do nothing by default.
		}
	
		/**
		 * Add beans that implement ApplicationListener as listeners.
		 * Doesn't affect other listeners, which can be added without being beans.
		 */
		protected void registerListeners() {
			// Register statically specified listeners first.
			for (ApplicationListener<?> listener : getApplicationListeners()) {
				getApplicationEventMulticaster().addApplicationListener(listener);
			}
	
			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let post-processors apply to them!
			String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
			for (String listenerBeanName : listenerBeanNames) {
				getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
			}
	
			// Publish early application events now that we finally have a multicaster...
			Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
			this.earlyApplicationEvents = null;
			if (earlyEventsToProcess != null) {
				for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
					getApplicationEventMulticaster().multicastEvent(earlyEvent);
				}
			}
		}
	
		......
		......
		......
		
		/**
		 * Finish the refresh of this context, invoking the LifecycleProcessor's
		 * onRefresh() method and publishing the
		 * {@link org.springframework.context.event.ContextRefreshedEvent}.
		 */
		protected void finishRefresh() {
			// Initialize lifecycle processor for this context.
			initLifecycleProcessor();
	
			// Propagate refresh to lifecycle processor first.
			getLifecycleProcessor().onRefresh();
	
			// Publish the final event.
			publishEvent(new ContextRefreshedEvent(this));
	
			// Participate in LiveBeansView MBean, if active.
			LiveBeansView.registerApplicationContext(this);
		}
	
		......
		......
		......
	
		/**
		 * DisposableBean callback for destruction of this instance.
		 * <p>The {@link #close()} method is the native way to shut down
		 * an ApplicationContext, which this method simply delegates to.
		 * @see #close()
		 */
		@Override
		public void destroy() {
			close();
		}
	
		/**
		 * Close this application context, destroying all beans in its bean factory.
		 * <p>Delegates to {@code doClose()} for the actual closing procedure.
		 * Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
		 * @see #doClose()
		 * @see #registerShutdownHook()
		 */
		@Override
		public void close() {
			synchronized (this.startupShutdownMonitor) {
				doClose();
				// If we registered a JVM shutdown hook, we don't need it anymore now:
				// We've already explicitly closed the context.
				if (this.shutdownHook != null) {
					try {
						Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
					}
					catch (IllegalStateException ex) {
						// ignore - VM is already shutting down
					}
				}
			}
		}
	
		/**
		 * Actually performs context closing: publishes a ContextClosedEvent and
		 * destroys the singletons in the bean factory of this application context.
		 * <p>Called by both {@code close()} and a JVM shutdown hook, if any.
		 * @see org.springframework.context.event.ContextClosedEvent
		 * @see #destroyBeans()
		 * @see #close()
		 * @see #registerShutdownHook()
		 */
		protected void doClose() {
			if (this.active.get() && this.closed.compareAndSet(false, true)) {
				if (logger.isInfoEnabled()) {
					logger.info("Closing " + this);
				}
	
				LiveBeansView.unregisterApplicationContext(this);
	
				try {
					// Publish shutdown event.
					publishEvent(new ContextClosedEvent(this));
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
				}
	
				// Stop all Lifecycle beans, to avoid delays during individual destruction.
				try {
					getLifecycleProcessor().onClose();
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
				}
	
				// Destroy all cached singletons in the context's BeanFactory.
				destroyBeans();
	
				// Close the state of this context itself.
				closeBeanFactory();
	
				// Let subclasses do some final clean-up if they wish...
				onClose();
	
				this.active.set(false);
			}
		}
		......
		......
		......
	}

六、知识点

  1. AbstractApplicationContext 类是 ApplicationEventPublisher 接口的实现,是一个事件的发布者
    在这里插入图片描述
  2. AbstractApplicationContext 类中方法 refresh 中调用方法 initApplicationEventMulticaster() 初始化一个事件广播者
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值