在SpringBoot应用启动过程的关键阶段触发,包括starting(启动开始)environmentPrepared(环境准备完成)contextPrepared(上下文准备完成)contextLoaded(上下文加载完成)started(启动完成)ready(应用准备就绪) failed(启动失败),以便在每个阶段执行自定义逻辑。

2、示例

下面是一个简单的 SpringApplicationRunListener 实现示例:

SpringBoot应用启动监听器需要SpringApplication和String[]参数,以便访问应用上下文和传递启动时的命令行参数,从而实现对启动事件的定制和响应。所以自定义的启动监听器也需要一个标准的构造函数,才能统一的创建SpringApplicationRunListener实例。

public class MyApplicationRunListener implements SpringApplicationRunListener {
    public MyApplicationRunListener(SpringApplication application, String[] args) {
        // 构造函数必须包含 SpringApplication 和 String[] 参数
    }
    @Override
    public void starting() {
        System.out.println("应用启动开始...");
    }
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("环境已准备好...");
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("ApplicationContext 已准备完成...");
    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("ApplicationContext 已加载...");
    }
    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("应用已启动...");
    }
    @Override
    public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("应用正在运行...");
    }
    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("应用启动失败:" + exception.getMessage());
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

需要在META-INF/spring.factories文件中进行注册:

org.springframework.boot.SpringApplicationRunListener=com.example.MyApplicationRunListener
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

启动服务

在这里插入图片描述

  可以看到除了异常启动失败的触发,一共有6个触发的地方。本文就只介绍启动开始阶段触发的操作。

二、事件和广播器

  在进入启动方法之前,需要先了解下事件和广播器。

1、事件SpringApplicationEvent

1.1、介绍

  SpringApplicationEvent是SpringBoot框架中的一个重要概念,属于事件发布/订阅机制的一部分。它允许应用程序在特定事件发生时发布事件,其他组件可以订阅这些事件并做出相应的处理。这种机制提高了系统的解耦性和可扩展性。

类图如下:

在这里插入图片描述

1.1.1、EventObject
// 所有事件状态对象的根类。所有的事件对象都应从此类派生
public class EventObject implements Serializable {
    private static final long serialVersionUID = 5516075349620653480L;
	// 事件最初发生的对象。使用 transient 修饰符表明该字段不会被序列化。
    protected transient Object source;
	// 构造一个原型事件
    public EventObject(Object source) {
        // 检查 source 是否为 null,避免不合法的事件源
        if (source == null)
            throw new IllegalArgumentException("null source");
        // 设置事件的源对象
        this.source = source;
    }
	// 获取事件最初发生的对象
    public Object getSource() {
        return source;
    }
	// 返回该EventObject的字符串表示形式(包括类的名称和事件源信息)
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1.1.2、ApplicationEvent
// 抽象类,应用事件的基类。所有应用事件都应扩展此类
public abstract class ApplicationEvent extends EventObject {
	private static final long serialVersionUID = 7099057708183571937L;
	// 事件发生的系统时间(时间戳)
	private final long timestamp;
	// 构造
	public ApplicationEvent(Object source) {
		// 调用父类 EventObject 的构造函数,传递事件源
		super(source);
		// 设置事件的时间戳,记录事件创建时的系统时间
		this.timestamp = System.currentTimeMillis();
	}
	// 此构造函数通常用于测试场景
	public ApplicationEvent(Object source, Clock clock) {
		// 调用父类 EventObject 的构造函数,传递事件源
		super(source);
		// 设置时间戳为由指定 Clock 提供的时间
		this.timestamp = clock.millis();
	}
	// 返回事件发生的时间
	public final long getTimestamp() {
		return this.timestamp;
	}
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1.1.3、SpringApplicationEvent
public abstract class SpringApplicationEvent extends ApplicationEvent {
	// 启动应用时传递的命令行参数
	private final String[] args;
	// 构造一个新的 SpringApplicationEvent
	public SpringApplicationEvent(SpringApplication application, String[] args) {
		// 调用父类 ApplicationEvent 的构造方法,设置事件源为 SpringApplication 实例
		super(application);
		this.args = args;
	}
	// 获取触发此事件的 SpringApplication 实例
	public SpringApplication getSpringApplication() {
		// 将源对象强制转换为 SpringApplication 类型并返回
		return (SpringApplication) getSource();
	}
	// 获取启动应用时传递的命令行参数
	public final String[] getArgs() {
		return this.args;
	}
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

1.2、常见的子事件

  1. ApplicationStartingEvent
  2. ApplicationEnvironmentPreparedEvent
  3. ApplicationContextInitializedEvent
  4. ApplicationPreparedEvent
  5. ApplicationStartedEvent
  6. ApplicationReadyEvent
  7. ApplicationFailedEvent

2、广播器ApplicationEventMulticaster

  ApplicationEventMulticaster的核心功能是管理监听器(ApplicationListener)的注册和注销,以及将事件广播给合适的监听器

类图如下:默认唯一的抽象子类和实现类

在这里插入图片描述

2.1、ApplicationEventMulticaster

public interface ApplicationEventMulticaster {
	// 添加一个监听器实例,以接收所有事件通知
	void addApplicationListener(ApplicationListener<?> listener);
	// 通过指定Bean名称添加一个监听器Bean,以接收所有事件通知
	void addApplicationListenerBean(String listenerBeanName);
	
	// 从通知列表中移除一个监听器
	void removeApplicationListener(ApplicationListener<?> listener);
	// 通过指定 Bean 名称,从通知列表中移除一个监听器 Bean
	void removeApplicationListenerBean(String listenerBeanName);

	// 从已注册的实例集合中移除所有匹配的监听器(predicate是一个条件过滤器)
	void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
	// 从已注册的监听器Bean名称集合中移除所有匹配的监听器 Bean(predicate是一个条件过滤器)
	void removeApplicationListenerBeans(Predicate<String> predicate);
	// 移除此广播器注册的所有监听器
	void removeAllListeners();

	// 将给定的应用事件广播到合适的监听器
	void multicastEvent(ApplicationEvent event);
	// 将给定的应用事件广播到合适的监听器,eventType为事件的类型
	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

2.2、AbstractApplicationEventMulticaster

public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    // 默认监听器检索器
    private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
    // 用于缓存监听器检索的缓存Map
    final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

	...
	
    // 添加监听器
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.defaultRetriever) {
            Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
            if (singletonTarget instanceof ApplicationListener) {
                this.defaultRetriever.applicationListeners.remove(singletonTarget);
            }
            this.defaultRetriever.applicationListeners.add(listener);
            this.retrieverCache.clear();
        }
    }
    // 添加监听器Bean
    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
        synchronized (this.defaultRetriever) {
            this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
            this.retrieverCache.clear();
        }
    }
    // 省略类似添加和移除方法
	...
	
	// defaultRetriever持有所有注册的监听器集合
	private class DefaultListenerRetriever {
	    // 应用程序监听器的集合,存储直接注册的监听器实例
	    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
	    // 应用程序监听器的名称集合,存储监听器bean的名称,用于延迟加载
	    public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
	    ...
	 }
	
	// retrieverCache持有所有注册的监听器集合
	private class CachedListenerRetriever {
		// 应用程序监听器的集合
		@Nullable
		public volatile Set<ApplicationListener<?>> applicationListeners;
		// 应用程序监听器的名称集合
		@Nullable
		public volatile Set<String> applicationListenerBeans;
		...
	}
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

2.3、SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    // 任务执行器,用于异步执行监听器,默认为空(同步执行)
    @Nullable
    private Executor taskExecutor;
    // 错误处理器,用于处理监听器抛出的异常,默认为空
    @Nullable
    private ErrorHandler errorHandler;
    // 懒加载的日志记录器,用于在需要时记录日志
    @Nullable
    private volatile Log lazyLogger;

	// 创建一个新的 SimpleApplicationEventMulticaster 实例
    public SimpleApplicationEventMulticaster() {
    }

    // 创建SimpleApplicationEventMulticaster时候
    // 设置一个异步任务执行器,就可以实现多线程执行了
    public void setTaskExecutor(@Nullable Executor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

	...
	
	// 将给定的应用事件广播到合适的监听器(核心方法)
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(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);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
        	// 调用监听器的核心方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException ex) {
			...
        }
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

三、启动方法

  启动方法实际就是遍历调用应用启动监听器SpringApplicationRunListener实现类(只有一个EventPublishingRunListener)的starting方法。

1、广播事件到应用监听器

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    // SpringApplication 对象,用于访问应用的配置信息和监听器
    private final SpringApplication application;
    // 启动参数
    private final String[] args;
    // 初始事件广播器,用于在启动阶段广播事件
    private final SimpleApplicationEventMulticaster initialMulticaster;
    /**
     * 初始化一个事件广播器,并将所有应用监听器添加到广播器中。
     */
    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        // 初始化一个事件广播器
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 将 SpringApplication 中的所有监听器添加到初始广播器中
        for (ApplicationListener<?> listener : application.getListeners()) {
        	// 实际调用AbstractApplicationEventMulticaster的添加方法
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
	// 返回该监听器的执行顺序。数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0; // 优先级最高
    }
	// 在应用启动初期调用
    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        // 将应用开始事件广播到合适的监听器
        this.initialMulticaster
            .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
    }
	
	// 省略其他阶段的代码,后面走到对应流程在看具体操作
	...
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在这里插入图片描述

// 一个处理应用事件的监听器接口。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	// 响应应用事件的方法。当发布指定类型的事件时,该方法会被调用,
	// 以便执行相应的业务逻辑。
	void onApplicationEvent(E event);
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

2、根据事件类型匹配监听器

// AbstractApplicationEventMulticaster类方法
protected boolean supportsEvent(
        ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

    // 检查监听器是否为 GenericApplicationListener 类型
    // 如果是,则直接使用;如果不是,则将其包装为 GenericApplicationListenerAdapter
    GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
            (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));

    // 检查监听器是否支持指定的事件类型和来源类型
    // smartListener.supportsEventType(eventType): 验证监听器是否支持指定的事件类型
    // smartListener.supportsSourceType(sourceType): 验证监听器是否支持指定的事件来源类型
    return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

  监听器分为两种形式,实现ApplicationListener<指定事件类型>或实现GenericApplicationListener(通过supportsEventType方法-支持事件类型和supportsSourceType方法-支持事件来源)。

在这里插入图片描述

2.1、实现ApplicationListener<指定事件类型>

  如果监听器是普通方式实现ApplicationListener<指定事件类型>,那么会将监听器包装为GenericApplicationListenerAdapter,核心内容就是解析出指定事件类型的实际类型

在这里插入图片描述

验证监听器是否支持指定的事件类型

在这里插入图片描述

验证监听器是否支持指定的事件来源类型

在这里插入图片描述

2.2、实现GenericApplicationListener

  如果监听器是实现GenericApplicationListener方法,那么匹配事件类型和来源都在监听器内部实现,如下以LoggingApplicationListener例如下:

在这里插入图片描述

3、匹配到的监听器

在这里插入图片描述

  LoggingApplicationListener用于初始化日志系统,根据application.properties或环境配置设置日志级别和格式(Spring Boot 的日志系统初始化通常是由该监听器负责)。具体内容后续文章单独讲。

  至于BackgroundPreinitializer和DelegatingApplicationListener虽然匹配上了,但是这个启动开始阶段什么都没有做,后续其他阶段有操作时候再说。

总结

  1. 入口分析
  2. SpringApplicationRunListener详细分析
  3. 事件和广播器分析
  4. 启动方法解析
注:本文转载自blog.csdn.net的哪 吒的文章"https://blog.csdn.net/guorui_java/article/details/132255331"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!