【源碼解讀系列五】深入剖析Springboot啓動原理的底層源碼

寫在前面: 我是 揚帆向海,這個暱稱來源於我的名字以及女朋友的名字。我熱愛技術、熱愛開源、熱愛編程。技術是開源的、知識是共享的

這博客是對自己學習的一點點總結及記錄,如果您對 Java算法 感興趣,可以關注我的動態,我們一起學習。

用知識改變命運,讓我們的家人過上更好的生活

相關文章:

【SpringBoot 系列】史上最全的springboot學習教程


本文通過剖析源碼,對Spring Boot(基於2.x版本)的啓動過程進行深入的理解

一、入口類及其源碼剖析

入口類

@SpringBootApplication
public class DevServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(DevServiceApplication.class,args);
    }
}

在這裏插入圖片描述
首先從註解入手,進行分析:

@SpringBootApplication 註解

Spring Boot應用標註在某個類上說明這個類是SpringBoot的主配置類,SpringBoot就應該運行這個類的main方法來啓動SpringBoot應用

源碼剖析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

從源碼可以看出,這個註解是@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan這三個註解的組合

① @SpringBootConfiguration

Spring Boot的配置類;標註在某個類上,表示一個類提供了Spring Boot應用程序

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

@Configuration:配置類上來標註這個註解;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

注意

配置類相當於配置文件;配置類也是容器中的一個組件,它使用了@Component這個註解。

② @EnableAutoConfiguration

告訴SpringBoot開啓自動配置功能,這樣自動配置才能生效
藉助@import,掃描並實例化滿足條件的自動配置的bean,然後加載到IOC容器中

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@AutoConfigurationPackage:自動配置包
@Import(EnableAutoConfigurationImportSelector.class):給容器中導入組件

在這裏插入圖片描述

使用@EnableAutoConfiguration
這個註解開啓自動掃描,然後使用select選擇挑選滿足條件的文件,並且使用SpringFactoriesLoader進行實例化。最後加載到IOC容器裏面,即ApplicationContext中。

③ @ComponentScan

@ComponentScan就是自動掃描並加載符合條件的組件(比如@Component和@Repository等)或者bean定義,最終將這些bean定義加載到IOC容器中去 。

二、實例化SpringApplication對象的源碼剖析

源碼剖析

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 初始化資源加載器
	this.resourceLoader = resourceLoader;
	// 資源加載類不能爲 null
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 初始化加載資源類集合並去重
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推斷應用程序是不是web應用
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 設置初始化器(Initializer)
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 設置監聽器 
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 推斷出主應用入口類
	this.mainApplicationClass = deduceMainApplicationClass();
}

其中,在推斷應用程序是不是web應用的時候調用了deduceFromClasspath() 方法

源碼剖析

static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			// springboot2.0提出的響應式web應用	
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			// 如果兩個包路徑都沒有的話,就是普通應用
			if (!ClassUtils.isPresent(className, null)) {
				// 普通的應用
				return WebApplicationType.NONE;
			}
		}
		// 其實最後返回的就是這個servlet,因爲是web應用
		return WebApplicationType.SERVLET;
	}

1. 設置初始化器(Initializer)

initializers 是 SpringApplication 中的一個實例屬性

源碼剖析

/**
 * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
 * {@link ApplicationContext}.
 * @param initializers the initializers to set
 */
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<>(initializers);
}

initailizer實現了ApplicationContextInitializer接口

源碼剖析

/**
 * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
 * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
 *
 * <p>Typically used within web applications that require some programmatic initialization
 * of the application context. For example, registering property sources or activating
 * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
 * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
 * for declaring a "contextInitializerClasses" context-param and init-param, respectively.
 *
 * <p>{@code ApplicationContextInitializer} processors are encouraged to detect
 * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
 * implemented or if the @{@link org.springframework.core.annotation.Order Order}
 * annotation is present and to sort instances accordingly if so prior to invocation.
 *
 * @author Chris Beams
 * @since 3.1
 * @param <C> the application context type
 * @see org.springframework.web.context.ContextLoader#customizeContext
 * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
 * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
 * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
 */
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	 // 把初始化的ApplicationContextInitializer實現類加載到SpringApplication中
	void initialize(C applicationContext);

}

總結:

  • ApplicationContextInitializer接口的作用,在Spring上下文被刷新之前進行初始化的操作。典型地比如在Web應用中,註冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring爲了在不同環境下(如DEV,TEST,PRODUCTION等),加載不同的配置項而抽象出來的一個實體。
  • 調用initialize()方法,把初始化的ApplicationContextInitializer實現加載到SpringApplication中

通過getSpringFactoriesInstances(
ApplicationContextInitializer.class)方法獲得實現類

源碼剖析

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
	// 使用 Set保存names
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// 根據names進行實例化
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	// 對實例進行排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

2. 設置監聽器

源碼剖析

/**
 * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
 * and registered with the {@link ApplicationContext}.
 * @param listeners the listeners to set
 */
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
	this.listeners = new ArrayList<>(listeners);
}

繼承了ApplicationListener()接口

源碼剖析

/**
 * Interface to be implemented by application event listeners.
 *
 * <p>Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an {@code ApplicationListener} can generically declare
 * the event type that it is interested in. When registered with a Spring
 * {@code 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 {@code ApplicationEvent} subclass to listen to
 * @see org.springframework.context.ApplicationEvent
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.event.EventListener
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

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

}

總結

在這裏使用到了觀察者模式,有一個被觀察者和許多觀察者,當被觀察者的狀態發生改變時,要通知所有的觀察者做一些操作。

3. 推斷主應用入口類

源碼剖析

private Class<?> deduceMainApplicationClass() {
	try {
		// 構造一個異常類
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			// 通過main的棧幀推斷出入口類的名字
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

三、run() 方法源碼剖析

源碼剖析

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
	// 記時器,統計應用啓動的時間
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	// 初始化應用上下文和異常報告集合
	ConfigurableApplicationContext context = null;
	// SpringBootExceptionReporter 是異常處理器,啓動的時候通過它把異常信息展示出來
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	 // 設置系統屬性java.awt.headless的值,默認爲true
	configureHeadlessProperty();
	// 監聽器,SpringApplicationRunListeners實際上是一個集合
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 回調所有的獲取SpringApplicationRunListener.starting()方法
	listeners.starting();
	try {
		// 初始化默認參數
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 準備 Spring 環境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// 創建環境完成後回調,配置bean
		configureIgnoreBeanInfo(environment);
		// 打印器,springboot啓動的時候會打印springboot的標誌以及對應的版本
		Banner printedBanner = printBanner(environment);
		// 創建Spring應用上下文,來決定創建web的ioc還是普通的ioc
		context = createApplicationContext();
		// 實例化異常報告器
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		//準備上下文環境
        // Spring上下文前置處理
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		// prepareContext運行完成以後回調所有的SpringApplicationRunListener的contextLoaded();
		// Spring上下文刷新,表示刷新完成,進行後續的一些操作
		refreshContext(context);
        // Spring上下文後置處理
		afterRefresh(context, applicationArguments);
		// 停止計時器
		stopWatch.stop();
		// 輸出日誌記錄的類名、時間信息
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		// 發佈應用上下文啓動完成事件
		listeners.started(context);
		// 執行所有 Runner 運行器
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		// 發佈應用上下文就緒事件
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	// 返回應用上下文
	return context;
}

1. 開啓計時器

開啓計時器,用來統計應用啓動的時間

public void start() throws IllegalStateException {
		// 傳入一個空字符串作爲當前任務的名稱
        this.start("");
    }

    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
        	// 如果當前任務名字不爲空,拋出異常
            throw new IllegalStateException("Can't start StopWatch: it's already running");
        } else {
        	// 否則,記錄當前任務的開始時間
            this.currentTaskName = taskName;
            this.startTimeNanos = System.nanoTime();
        }
    }
  • 首先,傳入一個空字符串作爲當前任務的名稱
  • 其次,判斷當前任務名是否空,如果爲空,則記錄當前應用啓動的開始時間

2. 設置系統屬性的值

系統屬性的值默認是true,系統屬性的值來源於System.getProperty()。

private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
				System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

3. 監聽器

private SpringApplicationRunListeners getRunListeners(String[] args) {
		// 類加載對應的監聽器
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 創建SpringApplicationRunListener實例
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

4. 初始化默認參數

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

5.創建 Spring 環境

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // 獲取環境。如果存在就直接返回,否則先創建一個再返回
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置環境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 準備監聽器環境
    listeners.environmentPrepared(environment);
    // 將環境綁定到SpringApplication上面
    bindToSpringApplication(environment);
    // 如果不是web應用環境,將環境轉換成StandardEnvironment
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    // 返回環境
    return environment;
}

總結

  • 獲取環境。如果存在就直接返回,否則先創建一個再返回
  • 配置環境
  • 準備監聽器環境
  • 將環境綁定到SpringApplication上面
  • 如果不是web應用環境,將環境轉換成StandardEnvironment
  • 最後返回環境

6. 打印器

springboot啓動的時候會打印springboot的標誌以及對應的版本

private Banner printBanner(ConfigurableEnvironment environment) {
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader());
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

7. 創建Spring應用上下文

protected ConfigurableApplicationContext createApplicationContext() {
	// 首先進行判斷有沒有指定的實現類
	Class<?> contextClass = this.applicationContextClass;
	// 如果沒有,則根據應用類型選擇
	if (contextClass == null) {
		try {
			// 根據webApplicationType的類型去反射創建ConfigurableApplicationContext的具體實例
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
		}
	}
	// 通過反射,得到創建的對象
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

總結

  • 首先進行判斷有沒有指定的實現類; 如果沒有,則根據應用類型選擇;
  • 根據webApplicationType的類型去反射創建ConfigurableApplicationContext的具體實例;
  • 最後通過反射,得到創建的對象

對於Web應用,上下文類型是DEFAULT_WEB_CONTEXT_CLASS。

8. 實例化異常報告器

用 getSpringFactoriesInstances() 方法,獲取配置的異常類名稱,並實例化所有的異常類。

源碼剖析

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	// 使用名稱並確保唯一,以防止重複
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

9. Spring上下文前置處理

源碼剖析

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// 給IOC容器設置一些環境屬性
	context.setEnvironment(environment);
	// 給IOC容器註冊一些組件
	postProcessApplicationContext(context);
	// 調用初始化方法
	applyInitializers(context);
	// 監聽器,觸發contextPrepared 事件
	listeners.contextPrepared(context);
	// 記錄啓動過程中的日誌
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	// 添加特定的單例beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	// 加載所有資源
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 加載啓動類,將啓動類注入到容器中去
	load(context, sources.toArray(new Object[0]));
	// 觸發contextLoaded 事件
	listeners.contextLoaded(context);
}

10. Spring上下文刷新

刷新完成以後,會進行後續的一些操作

源碼剖析

private void refreshContext(ConfigurableApplicationContext context) {
	// 調用父類的refresh操作
	refresh(context);
	if (this.registerShutdownHook) {
		try {
			// 註冊一個關閉容器時的鉤子函數,在JVM關機的時候關閉這個上下文。
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}

調用了registerShutdownHook()方法

/**
 * Register a shutdown hook {@linkplain Thread#getName() named}
 * {@code SpringContextShutdownHook} with the JVM runtime, closing this
 * context on JVM shutdown unless it has already been closed at that time.
 * <p>Delegates to {@code doClose()} for the actual closing procedure.
 * @see Runtime#addShutdownHook
 * @see ConfigurableApplicationContext#SHUTDOWN_HOOK_THREAD_NAME
 * @see #close()
 * @see #doClose()
 */
@Override
public void registerShutdownHook() {
	if (this.shutdownHook == null) {
		// No shutdown hook registered yet.
		this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
			@Override
			public void run() {
				synchronized (startupShutdownMonitor) {
					// 調用doClose方法,進行容器銷燬時的清理工作
					doClose();
				}
			}
		};
		Runtime.getRuntime().addShutdownHook(this.shutdownHook);
	}
}

11. Spring上下文後置處理

在Spring容器刷新上下文後進行調用,依次調用註冊的Runners。

/**
 * Called after the context has been refreshed.
 * @param context the application context
 * @param args the application arguments
 */
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	AnnotationAwareOrderComparator.sort(runners);
	// CommandLineRunner、ApplicationRunner 這兩個接口,是在容器啓動成功後的最後一步進行回調
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

CommandLineRunner、ApplicationRunner 這兩個接口,是在容器啓動成功後的最後一步進行回調

12. 停止計時器

做計時監聽器停止操作,並統計一些任務執行信息

public void stop() throws IllegalStateException {
    if (this.currentTaskName == null) {
        throw new IllegalStateException("Can't stop StopWatch: it's not running");
    } else {
        long lastTime = System.nanoTime() - this.startTimeNanos;
        this.totalTimeNanos += lastTime;
        this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
        if (this.keepTaskList) {
            this.taskList.add(this.lastTaskInfo);
        }

        ++this.taskCount;
        this.currentTaskName = null;
    }
}

13. 發佈Spring上下文啓動完成事件

void started(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.started(context);
	}
}

14. 執行所有 Runner 運行器

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	AnnotationAwareOrderComparator.sort(runners);
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

15. 發佈Spring上下文就緒事件

void running(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.running(context);
	}
}

觸發所有 SpringApplicationRunListener 監聽器的 running 事件的方法。


由於水平有限,本博客難免有不足,懇請各位大佬不吝賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章