潍坊网站建设潍坊,在网站上做漂浮,设计开发建设网站平台,网站内建设的发展Spring事件监听机制是Spring框架中的一种重要技术#xff0c;允许组件之间进行松耦合通信。通过使用事件监听机制#xff0c;应用程序的各个组件可以在其他组件不直接引用的情况下#xff0c;相互发送和接受消息。
需求
在技术派中有这样一个需求#xff0c;当发布文章或…
Spring事件监听机制是Spring框架中的一种重要技术允许组件之间进行松耦合通信。通过使用事件监听机制应用程序的各个组件可以在其他组件不直接引用的情况下相互发送和接受消息。
需求
在技术派中有这样一个需求当发布文章或者文章下线时会发布一个事件给SiteMap站点地图帮助搜索引擎有效的抓取和索引网站SiteMap监听到该事件后会进行更新。 事件监听的本质是观察者模式的应用包括事件、事件监听器、事件发布器等主要组件。 事件一个实现了ApplicationEvent类的对象代表了应用程序中某个特定的事件。我们可以根据需要创建自定义事件只要继承ApplicationEvent类并添相关的属性和方法就可以了。
事件监听器实现了ApplicationListenerE接口的对象其中E表示事件监听器需要处理的事件类型。监听器可以通过onApplicationEventE event方法处理接受到的事件另外也可以使用EventListener注解来简化事件监听器的实现技术派正是采用的这种方式。
事件发布器事件发布器负责将事件发布给所有关注该事件的监听器在Spring中ApplicationEventPublisher接口定义了事件发布的基本功能而ApplicationEventPublisherAware接口允许组件获取到事件发布器的引用。Spring的核心容器ApplicationContext实现了ApplicationEventPublisher接口因此在Spring应用中通常直接使用ApplicationContext作为事件发布器技术派采用该方式。 实例
第一步事件
创建自定义事件ArticleMsgEvent继承ApplicationEvent。 Getter
Setter
ToString
EqualsAndHashCode(callSuper true)
public class ArticleMsgEventT extends ApplicationEvent {private ArticleEventEnum type;private T content;public ArticleMsgEvent(Object source, ArticleEventEnum type, T content) {super(source);this.type type;this.content content;}
}类上的四个注解为lombok提供。两个字段
type枚举类型ArticleEventEnum代表事件的类型。
表示文章上线或者下线。
content泛型T表示事件的内容在本例中我们会传一个文章的ID。
source在构造方法里卖我们还会传一个Object类型的数据表示事件的来源也就是事件的发布者。
ApplicationEvent是Spring Framework框架中用于定义事件的基类。 第二步发布事件
定义SpringUtil工具类实现了ApplicationContexAware Component
public class SpringUtil implements ApplicationContextAware {private static ApplicationContext context;Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringUtil.context applicationContext;}/*** 发布事件消息** param event*/public static void publishEvent(ApplicationEvent event) {context.publishEvent(event);} 通过实现ApplicationContextAware接口可以让这个类在Spring容器启动时自动获得ApplicationContext引用。作为事件发布器Component可以让该类被Spring容器自动实例化和管理。
自动装配过程是通过Spring得ApplicationContextAwareProcessor类实现得它是一个后置处理器。在Spring容器初始化时他会检查所有得Bean如果Bean实现了ApplicationContextAware接口。他会调用setApplicationContext方法将ApplicationContext的引用传递给Bean。
第三步用事件
通过调用SpringUtil.publishEvent()发布事件。在ArticleSettingServiceImpl类中。
Override
public void updateArticle(ArticlePostReq req) {
ArticleDO article articleDao.getById(req.getArticleId());
if (article null) {return;
}if (StringUtils.isNotBlank(req.getTitle())) {article.setTitle(req.getTitle());
}
article.setShortTitle(req.getShortTitle());ArticleEventEnum operateEvent null;
if (req.getStatus() ! null) {article.setStatus(req.getStatus());if (req.getStatus() PushStatusEnum.OFFLINE.getCode()) {operateEvent ArticleEventEnum.OFFLINE;} else if (req.getStatus() PushStatusEnum.REVIEW.getCode()) {operateEvent ArticleEventEnum.REVIEW;} else if (req.getStatus() PushStatusEnum.ONLINE.getCode()) {operateEvent ArticleEventEnum.ONLINE;}// switch (req.getStatus()){// case 0 :// operateEvent ArticleEventEnum.OFFLINE;// break;// case 3 :// operateEvent ArticleEventEnum.REVIEW;// break;// case 2 :// operateEvent ArticleEventEnum.ONLINE;// break;// default:// break;// }}
articleDao.updateById(article);if (operateEvent ! null) {// 发布文章待审核、上线、下线事件SpringUtil.publishEvent(new ArticleMsgEvent(this, operateEvent, article.getId()));
}
} 第四步监听并处理事件
通过 EventListener注解来处理事件在SitemapServiceImpl类中可以看到。 /*** 基于文章的上下线自动更新站点地图** param event*/
EventListener(ArticleMsgEvent.class)
public void autoUpdateSiteMap(ArticleMsgEventLong event) {ArticleEventEnum type event.getType();if (type ArticleEventEnum.ONLINE) {addArticle(event.getContent());} else if (type ArticleEventEnum.OFFLINE || type ArticleEventEnum.DELETE) {rmArticle(event.getContent());}
}public void addArticle(Long articleId) {
RedisClient.hSet(SITE_MAP_CACHE_KEY, String.valueOf(articleId), System.currentTimeMillis());
}public void rmArticle(Long articleId) {RedisClient.hDel(SITE_MAP_CACHE_KEY, String.valueOf(articleId));
} 当ArticleMsgEvent类型的事件被发布时此方法自动被触发在该方法中首先获得事件的类型ArticleEventEnum枚举值然后根据事件类型执行相应的操作上线时将文章添加到SiteMap下线时从SiteMap中删除。
测试
这个就时技术派中的事件监听机制了。
启动Redis启动服务端启动admin端在后端找一篇文章下线文章。 就可以在debug模式下看到事件触发了。 原理分析
Spring事件监听机制涉及到s四个主要的类 事件对象ApplicationEvent
事件监听器ApplicationLisener事件监听器可以通过EventListener注解定义事件处理方法而无需实现ApplicationListener接口 事件发布者ApplicationEventPublisher在Spring中可以通过ApplicationEventPublisherAware接口或使用Autowired注解来注入ApplicationEventPublisher实例当事件被发布时Spring会自动调用已注册的ApplicationListener实现类得onApplicationEvent方法。 事件管理者ApplicationEventMulticaster管理监听器和发布事件通常由SimpleApplicationEventMulticaster类实现。他会遍历所有已经注册的监听器并调用他们的onApplicationEvent方法。 ApplicationEvent
ApplicationEvent继承了EventObject对象。 来看看ApplicationEvent的子类关系图 ApplicationEvent 有一个重要的子类 ApplicationContextEvent而ApplicationContextEvent 又有 4 个重要的子类:
ContextStartedEvent:当 Spring 容器启动时触发该事件。这意味着所有 Bean 都已加载并且 ApplicationContext 已初始化。
ContextStoppedEvent:当 Spring 容器停止时触发该事件。当容器关闭并停止处理请求时通常会触发此事件。
ContextRefreshedEvent:当 ApplicationContext 刷新时触发该事件。这表示所有Bean 都已创建并且已初始化所有单例 Bean(前提是它们在容器初始化时需要初始化)
ContextClosedEvent:当 Spring 容器关闭时触发该事件。这表示所有 Bean 都已销塾Spring 容器已清理资源并停止 ApplicationListener ApplicationListener继承EventListener接口并要求实现onApplicationEventE event方法。 FunctionalInterface
public interface ApplicationListenerE extends ApplicationEvent extends EventListener {void onApplicationEvent(E event);
}
onApplicationEvent(E event)方法:当发布某个事件时所有注册的ApplicationListener 实例的 onApplicationEvent 方法都会被调用。在这个方法中可以编写处理特定事件的逻辑。此方法接收一个类型为E的参数这是 ApplicationEvent 的子类表示触发的事件。 当 Spring 应用启动时Spring 会扫描所有的 Bean寻找使用了 EventListener 注解的方法。一旦找到这样的方法Spring 会为这些方法创建 ApplicationListener 实例并将其注册到 ApplicationEventMulticaster。 ApplicationEventMulticaster ApplicationEventMulticaster 是一个接口负责管理监听器和发布事件包含了注册监听器、移除监听器以及发布事件的方法。 Spring 容器中通常会有一个默认的实现如 SimpleApplicationEventMulticaster继承了AbstractApplicationEventMulticaster. AbstractApplicationEventMulticaster 主要实现了管理监听器的方法(上面接口的前 5 个方法)比如说 addApplicationListener。 public void addApplicationListener(ApplicationListener? listener) {Assert.notNull(listener, ApplicationListener must not be null);if (this.applicationEventMulticaster ! null) {this.applicationEventMulticaster.addApplicationListener(listener);}this.applicationListeners.add(listener);
}最核心的一句代码: this.defaultRetriever.applicationListeners.add(listener);其内部类 DefaultListenerRetriever 里面有两个集合用来记录维护事件监听器 这就和设计模式中的发布订阅模式一样了维护一个 List用来管理所有的订阅者当发布者发布消息时遍历对应的订阅者列表执行各自的回调 handler。 再来看 SimpleApplicationEventMulticaster 类实现的广播事件逻辑 multicastEvent 的主要作用是将给定的 ApplicationEvent 广播给所有匹配的监听器
首先通过检査 eventType 参数是否为 nul 来确定事件类型。如果 eventType 为null则使用 resolveDefaultEventType(event)方法从事件对象本身解析事件类型
获取 Executor它是一个可选的任务执行器用于在异步执行监听器时调用。如果没有配置 Executor则默认为 nul表示使用同步执行。
使用 getApplicationListeners(eventtype)方法获取所有匹配给定事件类型的监听器。
对于每个匹配的监听器检查是否有 Executor 配置。如果存在 Executor则使用executor.execute()方法将监听器的调用封装到一个异步任务中。如果没有配置Executor则直接同步调用监听器。
使用 invokeListener(listenerevent)方法调用监听器的 onApplicationEvent方法将事件传递给监听器。 通过这个实现SimpleApplicationEventMulticaster 可以将事件广播给所有关心该事件的监听器同时支持同步和异步执行模式。
最后调用 istener.onApplicationEvent(event);也就是我们通过实现接口ApplicationListener 的方式来实现监听器的 onApplicationEvent 实现逻辑。 ApplicationEventPublisher ApplicationEventPublisher 是一个接口用于将事件发布给所有感兴趣的监听器。 这个接口的实现类通常会将事件委托给
ApplicationEventMulticaster。在 Spring 中ApplicationContext 通常充当事件发布者它就实现了 ApplicationEventPublisher 接口。
ApplicationContext 的 publishEvent 方法的逻辑实现主要在类AbstractApplicationContext 中: 这段代码的主要逻辑在这: 这段代码的主要作用是在 ApplicationContext 初始化时处理应用程序事件的发布。当ApplicationContext 还没有完全初始化时例如在refresh()方法中earlyApplicationEvents 列表会被用来保存早期的事件。在这个阶段ApplicationEventMulticaster 还没有完全配置好因此无法直接发布事件。这些早期的事件将在 ApplicationContext 初始化完成后ApplicationEventMulticaster 配置好后通过 finishRefresh()方法中的 publishEvent(new ContextRefreshedEvent(this));发布。 当 ApplicationContext 初始化完成后earlyApplicationEvents 列表将被设置为 null。此时事件可以直接通过 getApplicationEventMulticaster().multicastEvent(applicationEventeventType)方法发布给所有匹配的监听器,
这个机制确保了在 ApplicationContext 初始化过程中产生的事件不会丢失而是在ApplicationContext 初始化完成后被正确地发布给所有感兴趣的监听器。
总结
这篇内容通过源码的形式讲解了 Spring 事件监听机制及其原理。