Spring编程常见错误–Spring Core篇

您所在的位置:网站首页 spring编程常见错误50 Spring编程常见错误–Spring Core篇

Spring编程常见错误–Spring Core篇

2024-07-14 08:42| 来源: 网络整理| 查看: 265

七、Spring事件常见错误 一.试图处理并不会抛出的事件(并不是所有的事件都执行了start方法,只有执行了start方法的事件,才可以被监听到)

Spring的事件模型 在这里插入图片描述

1、Spring 事件的三大组件 1.事件(Event)

用来区分和定义不同的事件,在 Spring 中,常见的如 ApplicationEvent 和 AutoConfigurationImportEvent,它们都继承于 java.util.EventObject。

2.事件广播器(Multicaster)

负责发布上述定义的事件。例如,负责发布 ApplicationEvent 的 ApplicationEventMulticaster 就是 Spring 中一种常见的广播器。

3.事件监听器(Listener)

负责监听和处理广播器发出的事件,例如 ApplicationListener 就是用来处理 ApplicationEventMulticaster 发布的 ApplicationEvent,它继承于 JDK 的 EventListene。

2、代码 @Slf4j @Component public class MyContextStartedEventListener implements ApplicationListener { public void onApplicationEvent(final ContextStartedEvent event) { log.info("{} received: {}", this.toString(), event); } }

这段代码定义了一个监听器 MyContextStartedEventListener,试图拦截 ContextStartedEvent。 ContextStartedEvent的抛出只发生在一处,位于方法AbstractApplicationContext.start()之中

@Override public void start() { getLifecycleProcessor().start(); publishEvent(new ContextStartedEvent(this)); }

也就是说,只有上述方法被调用,才会抛出 ContextStartedEvent。

Spring 启动方法中围绕 Context 的关键方法调用,代码如下:

public ConfigurableApplicationContext run(String... args) { //省略非关键代码 ConfigurableApplicationContext context = null; Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context);此处知识进行了刷新操作 afterRefresh(context, applicationArguments); //省略非关键代码

Spring 启动最终调用的是 AbstractApplicationContext.refresh,并不AbstractApplicationContext.start。在这样的残酷现实下,ContextStartedEvent 自然不会被抛出,不抛出,自然也不可能被捕获。所以这样的错误也就自然发生了。

3、问题修正 1. 误读事件(监听错了事件)

误以为,想要监听的事件是自己想要的事件,比如关于Context,关于context的真是调用其实是刷新操作,因此我们真正需要调用的不是start,而是refresh.

@Component public class MyContextRefreshedEventListener implements ApplicationListener { public void onApplicationEvent(final ContextRefreshedEvent event) { log.info("{} received: {}", this.toString(), event); } }

监听 ContextRefreshedEvent 而非 ContextStartedEvent。ContextRefreshedEvent 的抛出可以参考方法 AbstractApplicationContext.finishRefresh,它本身正好是 Refresh 操作中的一步。

protected void finishRefresh() { //省略非关键代码 initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); // Publish the final event. publishEvent(new ContextRefreshedEvent(this)); //省略非关键代码 } 2. 需要处理该事件(就是要监听这个是事件,哪怕是进行手动的调用)

这种情况下,我们真的需要去调用 AbstractApplicationContext#start 方法。

@RestController public class HelloWorldController { @Autowired private AbstractApplicationContext applicationContext; @RequestMapping(path = "publishEvent", method = RequestMethod.GET) public String notifyEvent(){ applicationContext.start(); return "ok"; }; }

当一个事件拦截不了时,我们第一个要查的是拦截的事件类型对不对,执行的代码能不能抛出它。

二.监听事件的体系不对 1、代码:处理ApplicationEnvironmentPreparedEvent 事件 @Slf4j @Component public class MyApplicationEnvironmentPreparedEventListener implements ApplicationListener { public void onApplicationEvent(final ApplicationEnvironmentPreparedEvent event) { log.info("{} received: {}", this.toString(), event); } }

经过案例一的经验,首先看这个事件的抛出有没有问题。如果抛出有问题,那么必然是无法监听到的。

@Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } 2、案例分析(定义的监听器并不能监听到 initialMulticaster 广播出的 ApplicationEnvironmentPreparedEvent)

这是在 Spring 事件处理上非常容易犯的一个错误,即监听的体系不一致。

关于 ApplicationEnvironmentPreparedEvent 的处理,它相关的两大组件是什么?

1.广播器

这个事件的广播器是 EventPublishingRunListener 的 initialMulticaster,代码参考如下:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { //省略非关键代码 private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { //省略非关键代码 this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } } 2.监听器 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); } }

个事件的监听器就存储在 SpringApplication#Listeners 中,调试下就可以找出所有的监听器,但是自定义的监听器并不存在在其中。

3、问题修正


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3