文章整理来源:Spring编程常见错误50例_spring_spring编程_bean_AOP_SpringCloud_SpringWeb_测试_事务_Data-极客时间
定义了一个监听器 MyContextStartedEventListener,试图拦截 ContextStartedEvent
@Slf4j
@Component
public class MyContextStartedEventListener implements ApplicationListener {
public void onApplicationEvent(final ContextStartedEvent event) {
log.info("{} received: {}", this.toString(), event);
}
}
解析:在 Spring Boot 中,这个事件的抛出只发生在一处,即位于方法 AbstractApplicationContext 的 start() 方法中
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
Spring 启动最终调用的是 AbstractApplicationContext#refresh,并不是 AbstractApplicationContext#start。因此,ContextStartedEvent 自然不会被抛出,自然也不可能被捕获。
解决:1. 将 ContextStartedEvent 改为 ContextRefreshedEvent
@Component
public class MyContextRefreshedEventListener implements ApplicationListener {
public void onApplicationEvent(final ContextRefreshedEvent event) {
log.info("{} received: {}", this.toString(), event);
}
}
2. 调用 AbstractApplicationContext#start 方法
@RestController
public class HelloWorldController {
@Autowired
private AbstractApplicationContext applicationContext;
@RequestMapping(path = "publishEvent", method = RequestMethod.GET)
public String notifyEvent(){
applicationContext.start();
return "ok";
};
}
--------------------------------------------
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
}
和上述案例相似,事件没有被监听到
@Slf4j
@Component
public class MyApplicationEnvironmentPreparedEventListener implements ApplicationListener {
public void onApplicationEvent(final ApplicationEnvironmentPreparedEvent event) {
log.info("{} received: {}", this.toString(), event);
}
}
解析:与 ApplicationEnvironmentPreparedEvent 的处理,它相关的两大组件 1. 广播器是 EventPublishingRunListener 的 initialMulticaster 2. 监听器 EventPublishingRunListener
通过 Debug 会发现这个事件的监听器就存储在 SpringApplication#Listeners 中,但其中没有 上述定义的 MyApplicationEnvironmentPreparedEventListener
实际上 定义的监听器并没有被放置在 META-INF/spring.factories 中
解决:1.在构建 Spring Boot 时,添加 MyApplicationEnvironmentPreparedEventListener
@SpringBootApplication
public class Application {
public static void main(String[] args) {
MyApplicationEnvironmentPreparedEventListener myApplicationEnvironmentPreparedEventListener = new MyApplicationEnvironmentPreparedEventListener();
SpringApplication springApplication = new SpringApplicationBuilder(Application.class).listeners(myApplicationEnvironmentPreparedEventListener).build();
springApplication.run(args);
}
}
2. 使用 META-INF/spring.factories,即在 /src/main/resources 下面新建目录 META-INF,然后新建一个对应的 spring.factories 文件
org.springframework.context.ApplicationListener=\
com.spring.puzzle.listener.example2.MyApplicationEnvironmentPreparedEventListener
部分事件监听器一直失效或偶尔失效,监听器 MySecondEventListener 有一半的概率并没有接收到任何事件
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
--------------------------------------------------
@Component
@Order(1)
public class MyFirstEventListener implements ApplicationListener {
Random random = new Random();
@Override
public void onApplicationEvent(MyEvent event) {
log.info("{} received: {}", this.toString(), event);
//模拟部分失效
if(random.nextInt(10) % 2 == 1)
throw new RuntimeException("exception happen on first listener");
}
}
--------------------------------------------------
@Component
@Order(2)
public class MySecondEventListener implements ApplicationListener {
@Override
public void onApplicationEvent(MyEvent event) {
log.info("{} received: {}", this.toString(), event);
}
}
解析:处理器的执行是顺序执行的,在执行过程中,如果一个监听器执行抛出了异常,则后续监听器就得不到被执行的机会了
@Override
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.onApplicationEvent(event);
}
catch (ClassCastException ex) {
//省略非关键代码
}
else {
throw ex;
}
}
}
因为没有设置什么 org.springframework.util.ErrorHandler,也没有绑定 Executor 来执行任务。因此,事件的执行是由同一个线程按顺序来完成的,任何一个报错,都会导致后续的监听器执行不了
解决:1. 在监听中使用 try catch 去处理
2. 定义 ErrorHandler
public static final ErrorHandler LOG_AND_SUPPRESS_ERROR_HANDLER = new LoggingErrorHandler();
private static class LoggingErrorHandler implements ErrorHandler {
private final Log logger = LogFactory.getLog(LoggingErrorHandler.class);
@Override
public void handleError(Throwable t) {
logger.error("Unexpected error occurred in scheduled task", t);
}
}