SpringBoot源码简读——1.1 SpringApplication

项目的开始

下面是一个Spring Boot项目启动的入口代码

@SpringBootApplication
public class BootApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(BootApplication.class, args);
    }
}

我们可以发现,所有的项目的开始都是从这个SpringApplication开始,这就是SpringBoot项目的开始。那么我们看看这个里面主要包含了哪些东西。

SpringApplication

run

这个方法就是我们main方法中启动项目使用的方法,一切的开始

    public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
	
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

方法最终是新建了SpringApplication容器,并且调用run方法执行了main方法的args参数(假如有的话)

新建容器——构造方法

根据参数新建sb项目的容器

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 资源加载器
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// java config类的数组
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// web应用类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// ApplicationContextInitializer 重启初始化的数组
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 监听器的数组
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 主类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

上面代码我们需要关注的几个方法

  • WebApplicationType.deduceFromClasspath 获得web应用类型
  • 初始化数组 setInitializers
  • 监听器数组 setInitializers
  • 获得主类 deduceMainApplicationClass

新建容器——run方法

根据之前代码我们可以知道新建完容器后,系统会马上执行run方法。下面就是run方法的内容

public ConfigurableApplicationContext run(String... args) {
		// StopWatch 一个时间统计工具
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置Headless
		configureHeadlessProperty();
		// 获得监听器,并开始监听
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			// 获得容器的Arguments
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 通过监听器获得并处理配置环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			// 配置环境
			configureIgnoreBeanInfo(environment);
			// 打印Banner 这个东西emmmmm…………
			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);
			// 执行applicationRunner的允许方法
			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. 执行监听操作,开始监听
  2. 获得主类mian方法中的参数,配合监听器获得缓存配置
  3. 配置环境
  4. 创建容器
  5. 收集异常信息
  6. 初始化容器
  7. 进行一次刷新,以及刷新后的后置处理
  8. 通知监听操作,监听结束
  9. 执行applicationRunner

新建容器,以及执行时的这些方法

获得web应用类型

主要根据classPath判断不同的应用

	/**
	 * 根据classpath判断类型
	 * @return
	 */
	static WebApplicationType deduceFromClasspath() {
		// DispatcherHandler
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
			// mvc的DispatcherServlet
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			// jersey的ServletContainer
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			// 内嵌的 Reactive Web 应用。例如说,Spring Webflux
			return WebApplicationType.REACTIVE;
		}
		// 遍历 ConfigurableWebApplicationContext,javax.servlet.Servlet
		for (String className : SERVLET_INDICATOR_CLASSES) {
			// 如果不存在
			if (!ClassUtils.isPresent(className, null)) {
				// 非内嵌的 Web 应用
				return WebApplicationType.NONE;
			}
		}
		// 内嵌的 Servlet Web 应用。例如说,Spring MVC
		return WebApplicationType.SERVLET;
	}

返回的三种类型,在springboot上的说明是

  • NONE APP不应作为Web应用程序运行,也不应启动嵌入式Web服务器
  • SERVLET APP应作为基于servlet的Web应用程序运行,并应启动嵌入式servlet Web服务器。
  • REACTIVE APP应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。

容器初始化对象

用来初始化ApplicationContextInitializer对象,容器初始化策略

    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 = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 加载在 `META-INF/spring.factories` 里的类名的数组
		// 在 META-INF/spring.factories 文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 创建对象们
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		// 进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

1.从线程中获得类加载器,
2.然后从META-INF/spring.factories中加载 ApplicationContextInitializer.class的实现类

获得监听器

用来初始化ApplicationListener对象,监听器接口,整个逻辑和初始化ApplicationContextInitializer相同

	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 逻辑参考 初始化监听器数组

获得主类

获得启动启动的主类

    private Class<?> deduceMainApplicationClass() {
		try {
			// 获得当前 StackTraceElement 数组
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			// 判断哪个执行了 main 方法
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

从异常堆栈中获得执行了main方法的类。

设置Headless

Headless模式是在缺少显示屏、键盘或者鼠标是的系统配置

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

获得监听器

获得目前执行的监听器,注意这和之前的监听器不同,之前的ApplicationListener不同,之前的为spring的监听器,这一块初始化的是springboot的


    // 获得监听器,并开始监听
	SpringApplicationRunListeners listeners = getRunListeners(args);
    
    // 获得所有SpringApplicationRunListener的实现类,
	// 然后报装成SpringApplicationRunListeners
    private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

获得容器参数,以及通过监听器获得环境配置

这一块感觉用的不算太多,逻辑也不算复杂

// 获得容器的Arguments
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);

public class DefaultApplicationArguments implements ApplicationArguments {

	private final Source source;

	private final String[] args;
    //...其他代码

}

配置环境参数

		// 通过监听器获得并处理配置环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
        // 配置环境
			configureIgnoreBeanInfo(environment);
获得环境参数

主要获得环境的参数配置

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 获得或者创建环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 进行环境配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 通知监听器,环境已经完成配置
		listeners.environmentPrepared(environment);
		// 环境绑定到容器中
		bindToSpringApplication(environment);
		// 如果不是自定义的环境,则进行类型转换
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

从上面可以看到代码主要分为四个部分:

  • 环境配置的创建
  • 数据的配置,这其中监听器有很重要的作用
  • 事件的发布
  • 数据的绑定

创建容器

还记得上面的webApplicationType么,这一步就是根据之前获得的webApplicationType来初始化不同的实现类。


            // 创建容器
			context = createApplicationContext();

	protected ConfigurableApplicationContext createApplicationContext() {
		// 获得上面定义的容器类
		Class<?> contextClass = this.applicationContextClass;
		// 如果为空,则重新进行一次web容器类型判断一次返回对应的容器类,默认使用DEFAULT_CONTEXT_CLASS
		if (contextClass == null) {
			try {
				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);
	}
	

初始化容器

在完成参数配置后,容器开始进行数据初始化

			// 进一步初始化容器
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 设置容器的环境
		context.setEnvironment(environment);
		// 设置容器的属性
		postProcessApplicationContext(context);
		// Initializers的初始化
		applyInitializers(context);
		// 通知监听器容器准备晚餐
		listeners.contextPrepared(context);
		// 进行日志打印
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 为bean工厂注册springApplicationArguments、springBootBanner
		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}
		// 获得所有的bean源
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 为容器加载bean
		load(context, sources.toArray(new Object[0]));
		// 通知监听器容器加载完成
		listeners.contextLoaded(context);
	}

我们可以看到,容器初始化主要处理的逻辑有:

  • 配置容器环境和属性的配置
  • 真正的初始化逻辑主要是ApplicationContextInitializer来进行初始化
  • 事件的发布
  • bean资源的加载

刷新容器

在完成参数设置后,刷新容器的配置。这一块主要还是涉及spring的逻辑。

			// 刷新容器
			refreshContext(context);

	private void refreshContext(ConfigurableApplicationContext context) {
		// 进行刷新
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				// 注册钩子ShutdownHook
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
	
		protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		// 执行容器的刷新操作
		((AbstractApplicationContext) applicationContext).refresh();
	}

刷新容器后置处理

		// 刷新容器的后置处理
		afterRefresh(context, applicationArguments);

通知监听器,容器启动结束

		// 通知监听器,容器启动结束
		listeners.started(context);

执行applicationRunner

		// 执行applicationRunner的允许方法
		callRunners(context, applicationArguments);
			
			
		private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		// 获得容器中所有的ApplicationRunner类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// 获得容器中所有的CommandLineRunner 类
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		// 排序
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			// 执行其对应的callRunner方法
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

通知监听器容器运行中

		// 通知监听器容器运行中
		listeners.running(context);

总结

上面就是我们在启动springboot项目的时候,spring的一系列操作了。后续我们根据SpringApplication的构造以及SpringApplication.run两个大块进行学习,看看里面具体逻辑。主要包含内容

  • 构造函数内方法(ApplicationContextInitializer和webApplicationType)
  • run方法监听器
  • run方法环境配置
  • run方法初始化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大·风

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

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

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

打赏作者

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

抵扣说明:

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

余额充值