Spring Boot启动原理解析

SpringBoot一般启动方式

main方法中:
SpringApplication.run(XXX.class, args);

过程

SpringApplication的静态方法提供了哪些服务呢?源码如下:

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

如注释所写,使用默认的设置和具体的class来创建一个可运行的Spring 上下文对象(英文不好,大体是这个意思)。
其中入参primarySource,即为入口函数所在的class。
是如何创建这个Spring上下文的呢,继续追踪被调用的run方法,源码如下:

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

很明显,原来是通过new,生成一个Spring上下文对象,然后再执行run方法。
这边需要关注两个点:

  1. 创建这个Spring上下文,过程是什么样的?
  2. 上下文里面的run方法做了哪些事情?

创建SpringApplication

首先,先来看下是如何创建这个Spring上下文的。查看SpringApplication的构造器:

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这里可以看到有两个参数,resourceLoader,外部传值为null,不用关心,primarySources即为main的class,这个也很好理解,从上面方法传入。
可以看到,这边主要做了这么几件事情:

  1. 初始化WebApplicationType枚举(none,servlet套接字,reactive响应式)
  2. setInitializers,初始化ApplicationContextInitializer集合,注:继承自ApplicationContextInitializer接口,用以在Spring容易refresh之前执行一个回调函数initialize。
  3. setListeners,初始化ApplicationListener监听器集合
  4. 初始化入口函数信息
获取web应用类型
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
			
static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

首先可以看到ClassUtils.isPresent中,所有的ClassLoader实参,均为false,故类型值,只可能是WebApplicationType.SERVLET

初始化构造器集合

可以看到,设置构造器集合的数据来源是getSpringFactoriesInstances方法,入参为ApplicationContextInitializer.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<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

做了这么几件事情:

  1. 获取了一个String的names集合
  2. 看方法名称可知,是根据这个String集合,创建了一堆的sprintFactory的实例对象
  3. 对所有实例对象进行排序(不清楚排序是为了做啥)

以上三步,2,3两步比较容易理解,不做多阐述,我们来详细讲解下步骤1,不多说,直接上源码:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

源码显示,会先调用loadFactoryNames方法,内部会再调用loadSpringFactories方法,获取一个Map,任何根据当前入参class,获取一个string集合。
那么,这边的factoryType是谁呢?其实就是刚才上面特意提到的ApplicationContextInitializer.class,其getName的结果为org.springframework.context.ApplicationContextInitializer
下面我们再继续研究下这个Map是怎么产生的,里面都有些啥。
第一步,先从cache中读取缓存,第一次进入,肯定为空,继续往下执行
第二步,classLoader.getResources,ClassLoader.getSystemResources,很明显,是从META-INF/spring.factories中读取资源文件
第三步,解析资源文件。

说明:

  1. SpringBoot会去遍历所有当前jar中所有路径为META-INF/spring.factories的文件,并会将其数据进行汇总
  2. 默认有两处包含spring.factory文件,分别为spring-boot,spring-boot-autoconfigure,如图:
    在这里插入图片描述
    可以看到,两个文件中,包含org.springframework.context.ApplicationContextInitializer一共有7条记录(5+2),所以上面用name去Map中获取配置的时候,一共可以取出7条记录来
初始化监听器集合

同理,与构造器初始化一致,调用同一个方法,共会初始化出11个监听器实例对象。请记住这个数字11,后面会很有用处。

初始化入口函数信息
private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

这个就更简单了,从当前的线程栈中获取main方法所在的class,并反射构建出它的Class对象。

至此,我们终于完成了一个spring上下文的构建,下面该是执行它的run方法了。

run

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			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. 创建一个StopWatch,并启动,来记录时间
  2. 声明一个应用上下文,此时为null
    在这里插入图片描述
    ConfigurableApplicationContext是applicationContext的子类,在此基础上,增加了一系列配置应用上下文,以及控制上下文生命周期的功能。
  3. 创建一个异常报告的集合,启动中的异常信息会往里面进行放入
  4. configureHeadlessProperty,设置一些服务器环境,在没有显示器,外部IO的情况下,也可以启动
  5. 创建spring运行时的监听器集合,并start

是不是有点懵呢,下面对一些步骤,具体来看下内部实现

创建运行时监听器集合SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

啦啦啦,又见到我们的老朋友getSpringFactoriesInstances了,还记得上面的7,11这两个数字吗,没错,就是这位老兄帮忙创建出来的。很明显,这次也是从我们的spring.factory中寻找key为SpringApplicationRunListener的配置,并将它们进行创建实例对象,我们看下配置文件中的定义:
在这里插入图片描述
所以这边会创建一个EventPublishingRunListener的监听器实例,并将该类赋值给一个包装类SpringApplicationRunListeners。

下面,是时候进行RunListener的start方法了:

private final SpringApplication application;

// 上面在创建EventPublishingRunListener时会执行它的构造方法,如下:
public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

// 开始strat
@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

首先,我们可以看到在这边,创建了一个event事件,传入参数是application成员变量,类型是SpringApplication,没错,这个application,就是我们在最前面开始就创建出来的spring上下文对象。

通过构造方法可知,initialMulticaster是我们new了一个SimpleApplicationEventMulticaster对象,用来监听事件。

下面,我们继续跟进它的multicastEvent方法:

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

我们发现了一个执行器executor,以及遍历了一个getApplicationListeners方法的返回值listener的集合。是不是有点眼熟呢,我们之前好像是有创建过listener呢,这个先放一放,我们进入这个方法内部去看下到底做了些啥:

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {

		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// Quick check for existing entry on ConcurrentHashMap...
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}

		if (this.beanClassLoader == null ||
				(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
			// Fully synchronized building and caching of a ListenerRetriever
			synchronized (this.retrievalMutex) {
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				Collection<ApplicationListener<?>> listeners =
						retrieveApplicationListeners(eventType, sourceType, retriever);
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		}
		else {
			// No ListenerRetriever caching -> no synchronization necessary
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}

入参有两个参数,一个event,一个是eventType,值的来源,都是上面multicastEvent方法传下来的ApplicationStartEvent。抛开代码中从缓存中读取数据的逻辑,真正获取listener的逻辑其实只有下面这一句话:

Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);

是不是很好奇这方法究竟干了些啥呢?继续往里跟进:

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {

		LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		synchronized (this.retrievalMutex) {
			listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
			listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
		}
		for (ApplicationListener<?> listener : listeners) {
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
		if (!listenerBeans.isEmpty()) {
			BeanFactory beanFactory = getBeanFactory();
			for (String listenerBeanName : listenerBeans) {
				try {
					Class<?> listenerType = beanFactory.getType(listenerBeanName);
					if (listenerType == null || supportsEvent(listenerType, eventType)) {
						ApplicationListener<?> listener =
								beanFactory.getBean(listenerBeanName, ApplicationListener.class);
						if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
							if (retriever != null) {
								retriever.applicationListenerBeans.add(listenerBeanName);
							}
							allListeners.add(listener);
						}
					}
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Singleton listener instance (without backing bean definition) disappeared -
					// probably in the middle of the destruction phase
				}
			}
		}
		AnnotationAwareOrderComparator.sort(allListeners);
		return allListeners;
	}

这么一大坨代码,是不是有点慌呢,不着急,慢慢往下看。
略过前面的三行声明变量的代码,看到有一个同步代码块,对两个集合进行了赋值,紧接着便是对两个集合进行了遍历,后面就将listeners的集合进行返回。是不是有点懵,这两个集合,究竟是在什么时候进行赋值的呢?还记得上面说过的EventPublishingRunListener构造方法吗?不记得了没关系,我再贴一下代码:
在这里插入图片描述
现在一下子就明白了吧,在构建EventPublishingRunListener时,就已经将最开始的Spring上下文中的创建的那11个Listener赋值给了this.defaultRetriever.applicationListeners,而this.defaultRetriever.applicationListenerBeans是没有做任何赋值的,所以先不考虑遍历listenerBeans的逻辑。我们先记住11这个数字。
ok,现在我们需要对这11个listeners进行遍历了,当supportsEvent方法返回true之后,即使我们需要保留下来的listener。跟进这个方法:

	protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
		GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
				(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
		return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
	}

方法接收一个ResolvableType参数,还记得我上面提到的那个event吗,没错,就是ApplicationStartEvent,通过执行方法获取:

private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
		return ResolvableType.forInstance(event);
	}

到此处,supportsEvent的逻辑就很明了了,就是判断当前的listener是否能够监听ApplicationStartEvent,如果能,则将该监听器进行保留。
下面,我们欢迎能够幸存下来的4个listener跟大家见面:
在这里插入图片描述

前面已经跟大家见过面的执行器executor,因为获取到了listener的幸运儿,他已经安奈不住要登场了。为了方便理解,再重新贴一下代码:
在这里插入图片描述
由于我们没有对taskExecutor进行初始化,直接执行invokeListener方法,里面会调用各个linster的onApplicationEvent方法,看了几个linster的实现,有进行事件注册的,也有执行其他非事件相关的事情。

可怜的执行器executor,因为没有人给他实例化,什么事情都干不了…

至此,SpringApplicationRunListener里面start了,并且所有能够监听ApplicationStartEvent事件的监听器,也start了(4个linster中,其实有2个,其实啥事都没有干)。

写到这边,已经跟上面run方法里的代码,间隔有点大了,下面我将run里面剩余的代码单独列出来进行解释

创建ApplicationArguments
run 方法部分代码:
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

这个ApplicationArguments究竟是何方神圣呢?进入ApplicationArguments的构造器,看到有this.source = new Source(args);里面new了一个source对象,入参是args,再次跟进source的super构造器方法:

public SimpleCommandLinePropertySource(String... args) {
		super(new SimpleCommandLineArgsParser().parse(args));
	}

我的天,在父类的构造器里面,又new了一个对象,按住烦躁不安的心,决定再跟进一次:

class SimpleCommandLineArgsParser {

	/**
	 * Parse the given {@code String} array based on the rules described {@linkplain
	 * SimpleCommandLineArgsParser above}, returning a fully-populated
	 * {@link CommandLineArgs} object.
	 * @param args command line arguments, typically from a {@code main()} method
	 */
	public CommandLineArgs parse(String... args) {
		CommandLineArgs commandLineArgs = new CommandLineArgs();
		for (String arg : args) {
			if (arg.startsWith("--")) {
				String optionText = arg.substring(2, arg.length());
				String optionName;
				String optionValue = null;
				if (optionText.contains("=")) {
					optionName = optionText.substring(0, optionText.indexOf("="));
					optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length());
				}
				else {
					optionName = optionText;
				}
				if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
					throw new IllegalArgumentException("Invalid argument syntax: " + arg);
				}
				commandLineArgs.addOptionArg(optionName, optionValue);
			}
			else {
				commandLineArgs.addNonOptionArg(arg);
			}
		}
		return commandLineArgs;
	}

}

这次终于没有再创建新的对象了,注意到是有调用parse方法的,看下返回值类型,CommandLineArgs。见名思意,返回的不就是命令行参数嘛,由此可以确定,创建这个类,是为了解析命令行参数,如“java -jar *.jar --server.port=9090”,解析出“server.port=9090”
一个问题,参数解析出来之后,存在哪里呢?一步步追中source的父类,最终定位到PropertySource里面的一个泛型变量:T source,针对命令行参数信息,此时的T即为CommandLineArgs类型。
在这里插入图片描述

创建环境ConfigurableEnvironment
run 方法部分代码:
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

一脸懵逼,这个环境指的是啥?跟进代码看下:

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}

首先调用getOrCreateEnvironment方法,用以拿取或者创建出一个环境信息,到底是怎么创建的呢?

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webEnvironment) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}

可以看到,在方法内部,是直接创建了一个标准的servlet环境,然后发现,它没有构造器!按照spring的一贯个性,很有可能构造器是在其父类中
在这里插入图片描述
在其父类AbstractEnvironment中发现了构造器:

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(String.format(
					"Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
		}
	}

熟悉的propertySources出现了!内部有调用一个虚方法customizePropertySources,这时在转到StandardServletEnvironment内部查看:

	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
		propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
		if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
			propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
		}
		super.customizePropertySources(propertySources);
	}

这里面,便是在前面解析出来的命令行参数的基础上,在添加了一些运行时环境参数

后面再对现在已经收集到的所有参数的集合进行一下简单处理后,下面就应该让这些配置生效了:

listeners.environmentPrepared(environment);

这个linsteners是谁?可不就是前面创建的SpringApplicationRunListeners
下面我们来看看这个方法:

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}
	
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

是不是感觉方法内部调用的 initialMulticaster.multicastEvent方法很眼熟?没错,之前在进行linster的start方法的时候,也是调用的这个方法,不用的是,start的时候,入参是new一个ApplicationStartedEvent事件,而这次是new一个ApplicationEnvironmentPreparedEvent事件。后面的逻辑也是类似了,在前面的那11个linster中找到能够监听ApplicationEnvironmentPreparedEvent的监听器。在前面的start中,我们11位参赛选手,只有4位最终保留下来,这次我们看看能有几位幸运儿。
在这里插入图片描述
哇哦,这次很不错,11位中有7位获得了保留权。

接下来,我们隆重介绍下第一位选手ConfigFileApplicationListener,我们先看下它的onApplicationEvent回调方法:
在这里插入图片描述
在这里插入图片描述
下面来解释下这位老兄这么一大堆的操作都干了些啥事:

  1. 在回调方法中调用onApplicationEnvironmentPreparedEvent方法,onApplicationEnvironmentPreparedEvent方法中将自身的引用放入了一个集合,后续遍历这个集合对象,再调用对象的postProcessEnvironment方法,也即是会调用老兄他自身的postProcessEnvironment方法
  2. 在调用自身的postProcessEnvironment又调用了addPropertySources方法,内部会new 一个Loader对象,并且调用它的load方法。
  3. 在load方法中,实例化了一个PropertySourcesLoader对象,沟通构造函数可知,又会去spring.factory中去寻找需要加载并实例化的类,一个是PropertiesPropertySourceLoader,另一个是YamlPropertySourceLoader。实例化之后,会执行一个内部的load方法。
  4. 执行内部load方法是,会调用getAllFileExtensions方法,可逻辑可知,是获取到了刚才实例化的PropertiesPropertySourceLoader和YamlPropertySourceLoader对象,再往下走,会最终执行到这两个对象的load方法。这边以PropertiesPropertySourceLoader为demo:
public class PropertiesPropertySourceLoader implements PropertySourceLoader {

	@Override
	public String[] getFileExtensions() {
		return new String[] { "properties", "xml" };
	}

	@Override
	public PropertySource<?> load(String name, Resource resource, String profile)
			throws IOException {
		if (profile == null) {
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			if (!properties.isEmpty()) {
				return new PropertiesPropertySource(name, properties);
			}
		}
		return null;
	}

}**加粗样式**

load方法中,会加载后缀名为"properties","xml"的文件,并解析出其中的配置信息。

也就是说,我们自己定义的配置文件,在此处进行加载,并解析,最终将配置结果,应用到环境中

Banner
Banner printedBanner = printBanner(environment);

打印一个Banner信息,没什么好说的。

创建应用上下文 ConfigurableApplicationContext
context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);

关于Spring的上下文构建,刷新等,详见后续文章

finish

在这里插入图片描述
和上面的逻辑类似,先是构建出一个事件ApplicationFailedEvent(启动失败),ApplicationReadyEvent(启动成功),然后又是在11个linster中寻找可以监听这个事件的linster,再进行回调

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值