1. 概述
本文,我们来补充 《精尽 Spring Boot 源码分析 —— SpringApplication》 文章,并未详细解析的 ApplicationListener 。
2. ApplicationListener
org.springframework.context.ApplicationListener
,应用事件监听器接口。代码如下:
// ApplicationListener.java /** * Interface to be implemented by application event listeners. * Based on the standard {@code java.util.EventListener} interface * for the Observer design pattern. * * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type * that it is interested in. When registered with a Spring ApplicationContext, events * will be filtered accordingly, with the listener getting invoked for matching event * objects only. * * @author Rod Johnson * @author Juergen Hoeller * @param <E> the specific ApplicationEvent subclass to listen to * @see org.springframework.context.event.ApplicationEventMulticaster */ public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); } |
- 如果对这块不了解的胖友,可以看看 《Spring 5 源码解析 —— Spring 框架中的事件和监听器》 文章。
2.1 SmartApplicationListener
org.springframework.context.event.SmartApplicationListener
接口,实现 ApplicationListener、Ordered 接口,是 Spring3.0 新增的接口,提供了事件类型和来源的判断接口方法。代码如下:
// SmartApplicationListener.java /** * Extended variant of the standard {@link ApplicationListener} interface, * exposing further metadata such as the supported event and source type. * * <p>For full introspection of generic event types, consider implementing * the {@link GenericApplicationListener} interface instead. * * @author Juergen Hoeller * @since 3.0 * @see GenericApplicationListener * @see GenericApplicationListenerAdapter */ public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered { /** * 事件类型 * Determine whether this listener actually supports the given event type. * @param eventType the event type (never {@code null}) */ boolean supportsEventType(Class<? extends ApplicationEvent> eventType); /** * 事件来源 * Determine whether this listener actually supports the given source type. * <p>The default implementation always returns {@code true}. * @param sourceType the source type, or {@code null} if no source */ default boolean supportsSourceType(@Nullable Class<?> sourceType) { return true; } /** * Determine this listener's order in a set of listeners for the same event. * <p>The default implementation returns {@link #LOWEST_PRECEDENCE}. */ default int getOrder() { return LOWEST_PRECEDENCE; } } |
2.1 GenericApplicationListener
org.springframework.context.event.GenericApplicationListener
,继承 ApplicationListener、Ordered 接口,是 Spring4.2 新增的接口,它增强了对泛型的支持,#supportsEventType(ResolvableType)
方法的参数采用的是可解析类型 ResolvableType 。代码如下:
ResolvableType是 Spring4 提供的泛型操作支持类,通过它可以很容易地获得泛型的实际类型信息,比如类级、字段级等等泛型信息。在 Spring4 的框架中,很多核心类内部涉及的泛型操作大都使用 ResolvableType 类进行处理。
// GenericApplicationListener.java /** * Extended variant of the standard {@link ApplicationListener} interface, * exposing further metadata such as the supported event and source type. * * <p>As of Spring Framework 4.2, this interface supersedes the Class-based * {@link SmartApplicationListener} with full handling of generic event types. * * @author Stephane Nicoll * @since 4.2 * @see SmartApplicationListener * @see GenericApplicationListenerAdapter */ public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered { /** * 事件类型【有变动】 * Determine whether this listener actually supports the given event type. * @param eventType the event type (never {@code null}) */ boolean supportsEventType(ResolvableType eventType); /** * 事件来源 * Determine whether this listener actually supports the given source type. * <p>The default implementation always returns {@code true}. * @param sourceType the source type, or {@code null} if no source */ default boolean supportsSourceType(@Nullable Class<?> sourceType) { return true; } /** * Determine this listener's order in a set of listeners for the same event. * <p>The default implementation returns {@link #LOWEST_PRECEDENCE}. */ default int getOrder() { return LOWEST_PRECEDENCE; } } |
3. SpringApplication 中的使用
在 SpringApplication 构造方法中,会调用 #getSpringFactoriesInstances(Class<T> type)
方法,获得 ApplicationListener 集合。代码如下:
// SpringApplication.java private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // <1> 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // <2> 创建对象们 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // <3> 排序对象们 AnnotationAwareOrderComparator.sort(instances); return instances; } |
-
<1>
处,加载指定 ApplicationListener 类型对应的,在META-INF/spring.factories
里的类名的数组。- 假设只在 Spring MVC 的环境下,
listeners
属性的结果如下图: - 艿艿整理了 Spring Boot 中,ApplicationContextInitializer 的实现类们,非常多。本文,我们就分享上述的 10 个。
- 假设只在 Spring MVC 的环境下,
-
<2>
处,创建对象们。 -
<3>
处,排序对象们。
4. ConfigFileApplicationListener
org.springframework.boot.context.config.ConfigFileApplicationListener
,实现 SmartApplicationListener、Ordered、EnvironmentPostProcessor 接口,实现 Spring Boot 配置文件的加载。
考虑到它非常重要,且复杂,我们单独在 《精尽 Spring Boot 源码分析 —— 配置加载》 详细解析。
5. AnsiOutputApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
,实现 ApplicationListener、Ordered 接口,在 Spring Boot 环境变量(environment
)准备完成以后运行,
如果你的终端支持 ANSI ,设置彩色输出会让日志更具可读性。
不了解“彩色输出”的胖友,可以看看 《Spring Boot日志管理》 文章。
代码如下:
// ConfigFileApplicationListener.java public class AnsiOutputApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { // <1> // if (true) { // return; // <X> // } ConfigurableEnvironment environment = event.getEnvironment(); // <2> 根据环境变量 spring.output.ansi.enabled 的值,设置 AnsiOutput.enabled 属性 Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class) .ifBound(AnsiOutput::setEnabled); // <3> 根据环境变量 "spring.output.ansi.console-available 的值,设置 AnsiOutput.consoleAvailable 属性 AnsiOutput.setConsoleAvailable(environment.getProperty("spring.output.ansi.console-available", Boolean.class)); } public int getOrder() { // Apply after ConfigFileApplicationListener has called EnvironmentPostProcessors return ConfigFileApplicationListener.DEFAULT_ORDER + 1; } } |
-
<1>
处,我们可以知道,监听的是 ApplicationEnvironmentPreparedEvent 事件。 -
<2>
处,根据环境变量"spring.output.ansi.enabled"
的值,设置AnsiOutput.enabled
属性。这块的逻辑,卡了艿艿很久,一点一点来说。-
首先,因为艿艿并没有细看 Binder 的实现代码,所以这块就卡了一会。简单来说,
Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
代码块的意思是,从environment
读取"spring.output.ansi.enabled"
对应的值,并转换成 AnsiOutput.Enabled 类型。其中,AnsiOutput.Enabled 的枚举值如下:// AnsiOutput#Enabled.java public enum Enabled { /** * 自动探测,根据是否支持 ANSI 的功能,来判断是否要彩色输出 * * 【默认值】 * * Try to detect whether ANSI coloring capabilities are available. The default * value for {@link AnsiOutput}. */ DETECT, /** * 总是开启 ANSI 彩色输出 * * Enable ANSI-colored output. */ ALWAYS, /** * 禁用 ANSI 彩色输出 * * Disable ANSI-colored output. */ NEVER }
-
貌似也没什么问题。但是,让艿艿闷逼的是,为什么结果会是
AnsiOutput.Enabled.ALWAYS
,在 IDEA 环境中。后来,在environment
中,一个名字是"systemProperties"
的 MapPropertySource 属性源,里面提供了"spring.output.ansi.enabled=always"
的配置。- 各种全文检索代码,貌似除了测试用例里,没有地方强制赋值了
"spring.output.ansi.enabled"
。 - 后来发现,
"systemProperties"
这个 MapPropertySource 属性源,读取的是System#getProperties()
方法,但是为啥里面会有"spring.output.ansi.enabled=always"
呢?目前的猜测是,IDEA 判断在 Spring Boot 环境下,自动添加进去的!- 然后,
.ifBound(AnsiOutput::setEnabled)
代码段,应该翻译成如下的代码,可能比较易懂:Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class) .ifBound(new Consumer<Enabled>() { public void accept(Enabled enabled) { AnsiOutput.setEnabled(enabled); } });
- 然后,
- 各种全文检索代码,貌似除了测试用例里,没有地方强制赋值了
-
即,
Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
返回的是 BindResult 对象,然后调用BindResult#ifBound(Consumer)
方法,将解析到的属性值,赋值到AnsiOutput.enabled
属性。
-
-
相关推荐
《Spring源码分析——BeanFactory》 在Java的IoC(Inversion of Control)和DI(Dependency Injection)领域,Spring框架扮演着至关重要的角色。BeanFactory是Spring的核心组件之一,它是容器的基石,负责管理应用...
《Spring源码分析——ApplicationContext》 在Java世界中,Spring框架是不可或缺的一部分,它以其强大的IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)特性,极大地...
《Spring Boot实战派》源码提供了丰富的学习材料,旨在帮助开发者深入理解并...通过分析《Spring Boot实战派》源码,读者不仅可以了解上述技术点,还能学习到如何在实际项目中应用这些技术,提升开发效率和代码质量。
《Spring5 源码分析(第 2 版)》是某Tom老师精心编写的深度解析文档,旨在帮助读者全面理解Spring5的核心机制和设计理念。Spring作为Java领域最为广泛应用的框架之一,其源码的深入理解对于开发者来说至关重要。这篇...
Spring Boot 框架提供了多种方式来执行容器加载完成后的特定操作,例如使用 ApplicationListener 接口或使用 @PostConstruct 注解。在本文中,我们将介绍使用 ApplicationListener 接口来执行容器加载完成后的特定...
TODO Spring注解驱动开发第38讲——你知道ApplicationListener的用法吗?
在本文中,我们将深入探讨如何在Spring Boot应用中集成Apache Kafka。Kafka是一个高吞吐量、分布式的发布/订阅消息系统,常用于构建实时数据管道和流处理应用程序。Spring Boot是一个简化Spring应用开发的框架,它...
- 编写一个启动类,启动类通常是一个包含 `main` 方法的 Java 类,并使用 `@SpringBootApplication` 注解来标识这个类作为 Spring Boot 应用的主类。 - 运行程序,Spring Boot 应用通常通过启动类中的 main 方法启动...
TODO Spring注解驱动开发第39讲——你不知道的ApplicationListener的原理
在本文中,我们将深入探讨Spring Boot的运行机制,特别是其源码层面的实现。Spring Boot简化了传统的Spring应用启动过程,使得开发人员可以快速构建可执行的Java应用。我们从标题和描述中了解到,我们将关注Spring ...
此外,Spring Boot还提供了缓存事件监听机制,通过实现`ApplicationListener<CacheEvictEvent>`或`ApplicationListener<CacheEventListener>`接口,可以在缓存操作前后进行相应的业务处理。 在实际应用中,我们还...
- 使用`@ServletComponentScan`注解,在Spring Boot的启动类上指定,以便扫描并注册带有`@WebListener`注解的Servlet监听器。 - 为了防止用户在同一个浏览器使用多个tab或窗口时导致在线人数统计重复计数,可以将...
内容概要:本文详细介绍了Spring Boot的整体启动流程,涵盖Bootstrapper, ApplicationContextInitializer, ApplicationListener, SpringApplicationRunListener, ApplicationRunner, CommandLineRunner等重要组件的...
Spring Boot提供了内置支持,可以在`application.properties`或`application.yml`中配置: ```properties # application.properties 示例 spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.user=...
4. 从 spring.factories 文件中找出 key 为 ApplicationListener 的类,并实例化后设置到 SpringApplication 的 listeners 属性中。 5. 找出 main 类,这里是 MyApplication 类。 在 initialize 方法中,...
在Spring Boot应用中,整合Listener主要是为了监听应用的生命周期事件,例如在应用启动或关闭时执行特定的操作。这里我们讨论两种整合Listener的方式。 ### 方式一:通过注解扫描完成Listener组件的注册 1. **创建...
- 在Spring Boot应用中,我们可以创建一个`@Component`,并使用`@EventListener`注解来监听特定事件,或者自定义一个`ApplicationListener`监听`ApplicationReadyEvent`,在应用启动后开始监听文件夹。 - 使用`@...
Spring for Apache Kafka提供了与Spring Boot整合的便利,通过引入`spring-kafka`和`spring-boot-starter`依赖,我们可以轻松地在Spring Boot应用中使用Kafka。添加以下依赖: ```xml <groupId>org.spring...
Spring Boot 应用启动原理分析 Spring Boot 是一个快速开发框架,它简化了构建和运行 Java 应用程序的过程。其核心特点之一是能够将应用直接打包成一个可执行的 JAR 或 WAR 文件,无需额外的 Web 服务器。本文将...