SpringBoot源码系列(一)——启动过程分析

一、main()方法调用

1. main()方法

@SpringBootApplication
public class SpringBootHelloworldQuickApplication {

    public static void main(String[] args) {
        // 先从main()方法入手,传入当前类
        SpringApplication.run(SpringBootHelloworldQuickApplication.class, args);
    }
}

2. SpringApplication.run()方法

run()方法会调用如下的方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {
    // primarySources是当前启动类
	return new SpringApplication(primarySources).run(args);
}

该方法分两步走:

  • 创建SpringApplication对象
  • 调用springApplicaiton对象的run()方法,其中args可以传递启动时需要的个性化参数

二、创建SpringApplication对象

1. SpringApplication()构造方法

该方法最终会调用SpringApplication的构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

	this.resourceLoader = resourceLoader;
    
	//保存主配置类SpringBootHelloworldQuickApplication.class
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

	//判断当前是否一个web应用,主要是判断是普通web应用、响应式web应用、非web应用
	this.webApplicationType = deduceWebApplicationType();

	//从类路径下找到META‐INF/spring.factories,找到其中配置的所有ApplicationContextInitializer,然后保存起来,重要!!!!
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

	//从类路径下找到ETA‐INF/spring.factories,找到其中配置的所有ApplicationListener,然后保存起来,重要!!!!
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

	//从多个配置类中找到有main方法的主配置类
	this.mainApplicationClass = deduceMainApplicationClass();
}

Debug调试获取到的ApplicationContextInitializerApplicationListener,启动时候会用到
这里写图片描述
这里写图片描述

2. deduceWebApplicationType()

private WebApplicationType deduceWebApplicationType() {

   /**
    **  如果程序中有org.springframework.web.reactive.DispatcherHandler这个类且没有
    ** org.springframework.web.servlet.DispatcherServlet和  
    ** org.glassfish.jersey.servlet.ServletContainer 这两类那么就是REACTIVE
    **/
	if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
			&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}

	//SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
	//       "org.springframework.web.context.ConfigurableWebApplicationContext" }
    // 如果程序中这两个类有一个不存在就是NONE
	for (String className : WEB_ENVIRONMENT_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
    // 如果上面都不是那就是SERVLET
	return WebApplicationType.SERVLET;
}

3. getSpringFactoriesInstances()

该方法内部,最终会调用loadSpringFactories()方法;

loadSpringFactories()方法内部会加载META-INF/spring.factories文件,这里加载的文件不仅包含项目中的,还包换我们项目环境所依赖的jar包中的META-INF/spring.factories文件。

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

	try {
		//获取当前ClassLoader下的所有包含META-INF/spring.factories文件的URL路
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));


		result = new LinkedMultiValueMap<>();

       	//遍历所有的包含META-INF/spring.factories文件URL集合
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);

           	//转换为Properties对象
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);

			//遍历META-INF/spring.factories文件中的所有属性
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				//如果一个接口希望配置多个实现类,可以使用','进行分割,将当前Key对应的值转换为List
				List<String> factoryClassNames = Arrays.asList(
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));

				//添加到返回对象中
				result.addAll((String) entry.getKey(), factoryClassNames);
			}
		}
		cache.put(classLoader, result);
		return result;
	} catch (IOException ex) {
		// 抛异常代码略
	}
}

4. deduceMainApplicationClass()

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

三、调用springApplicaiton对象的run()方法

调用run()方法代码入口

new SpringApplication(primarySources).run(args);

1. run()

public ConfigurableApplicationContext run(String... args) {
	// 创建一个用来方便的记录程序的运行时间的对象
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

	// 设置系统属性java.awt.headless,默认为true,运行在没有显示器和键盘的环境。
	configureHeadlessProperty();

	// 获取SpringApplicationRunListeners,从类路径下META‐INF/spring.factories
	SpringApplicationRunListeners listeners = getRunListeners(args);

	// 回调所有的获取SpringApplicationRunListener.starting()方法
	listeners.starting();
	try {

		//封装命令行参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

		// 创建并配置当前SpringBoot应用将要使用的Environment,
		// 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();
		// 表示环境准备完成
		ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
		configureIgnoreBeanInfo(environment);

		// 打印banner
		Banner printedBanner = printBanner(environment);

		// 创建ApplicationContext,该过程和Spring创建容器启动过程一样
		context = createApplicationContext();

		// 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的SpringBootExceptionReporter
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);

		// 准备应用上下文[整个容器的创建与启动以及bean的注入功能]
		prepareContext(context, environment, listeners, applicationArguments,printedBanner);

		// 刷新应用上下文[是实现spring-boot-starter-*的自动化配置的关键]
		refreshContext(context);

		// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,
		// ApplicationRunner先回调,CommandLineRunner再回调
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}

		// 启动应用程序中的所有监听器(创建SpringApplication对象过程中有获取到)
		listeners.started(context);

		// 调用应用程序中所有CommandLineRunner.class和ApplicationRunner.class的实现类的run方法
		callRunners(context, applicationArguments);
	} catch (Throwable ex) {
		// 抛异常代码略
	}

	try {
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}

	//整个SpringBoot应用启动完成以后返回启动的ioc容器;
	return context;
}
protected ConfigurableApplicationContext createApplicationContext() {
	return this.applicationContextFactory.create(this.webApplicationType);
}

// 根据webApplicationType类型来创建Spring容器
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
	try {
		switch (webApplicationType) {
		case SERVLET:
		    // 如果是SERVLET创建"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
			return new AnnotationConfigServletWebServerApplicationContext();
		case REACTIVE:
			// 如果是REACTIVE创建"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
			return new AnnotationConfigReactiveWebServerApplicationContext();
		default:
		    // 默认是创建org.springframework.context.annotation.AnnotationConfigApplicationContext
			return new AnnotationConfigApplicationContext();
		}
	} catch (Exception ex) {
		// 抛异常代码略
	}
};

四、总结

从上面源码可以看出,SpringApplicationrun方法主要执行以下逻辑:

  • 创建一个用来方便的记录程序的运行时间的对象
  • 设置系统属性java.awt.headless,默认为true,运行在没有显示器和键盘的环境
  • 加载并启动一系列SpringApplicationRunListener对象
  • 创建并配置当前SpringBoot应用将要使用的Environment
  • 如果SpringApplicationshowBanner属性被设置为true,则打印banner
  • 创建ApplicationContext
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的SpringBootExceptionReporter
  • 准备应用上下文[整个容器的创建与启动以及bean的注入功能]
  • 刷新应用上下文[是实现spring-boot-starter-\*的自动化配置的关键]
  • 调用ApplicationContextrefresh()方法,完成IoC容器可用的最后一步操作
  • 启动应用程序中的所有监听器
  • 调用应用程序中所有CommandLineRunner.classApplicationRunner.class的实现类的run方法
  • 发布事件

五、补充

重要的事件回调机制,配置在META-INF/spring.factories

ApplicationContextInitializer
SpringApplicationRunListener

只需要放在IOC容器中

ApplicationRunner
CommandLineRunner
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值