`
shinesuo
  • 浏览: 156121 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

Spring Boot 源码分析 —— ApplicationListener

 
阅读更多

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
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

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}.
	 */
	@Override
	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}.
	 */
	@Override
	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 属性的结果如下图:`listeners` 属性
    • 艿艿整理了 Spring Boot 中,ApplicationContextInitializer 的实现类们,非常多。本文,我们就分享上述的 10 个。
  • <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 {

	@Override
	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));
	}

	@Override
	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>() {
                @Override
                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 属性。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics