SpringApplication.run 源码分析

SpringBoot 程序启动入口

@SpringBootApplication
public class Springboot01DemoApplication {

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

点击SpringApplication.run查看源码

	/**
     * 运行 Spring 应用
     *
	 * 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}
	 *
	 */
	//调用静态类,参数对应的就是SpringbootDemoApplication.class以及main方法中的args
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	/**
     * 运行 Spring 应用
     *
	 * 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) {
	   //SpringApplication的启动由两部分组成:
		//1. 实例化SpringApplication对象
		//2. run(args):调用run方法
		return new SpringApplication(primarySources).run(args);
	}
  • 上述代码执行了两个操作:首先 new SpringApplication(primarySources), 执行run方法

new SpringApplication(primarySources),SpringApplication实例化的创建过程

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

		this.sources = new LinkedHashSet();
		this.bannerMode = Mode.CONSOLE;
		this.logStartupInfo = true;
		this.addCommandLineProperties = true;
		this.addConversionService = true;
		this.headless = true;
		this.registerShutdownHook = true;
		this.additionalProfiles = new HashSet();
		this.isCustomEnvironment = false;
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");

		//项目启动类 SpringbootDemoApplication.class设置为属性存储起来
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

		//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();

		// 设置初始化器(Initializer),最后会调用这些初始化器
		//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

		// 设置监听器(Listener)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

		// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

  • 从上述代码中可以看出SpringApplication的初始化过程主要分为四个部分,具体代码可往下看
  1. this.webApplicationType = WebApplicationType.deduceFromClasspath(),主要作用是设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
  2. this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)),设置初始化器(Initializer),在初始化器设置过程中,会使用SpringFactoriesLoader从META-INF/spring.factories文件获取所有可用的应用初始化器类ApplicationContextInitializer
  3. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)),设置监听器(Listener),与上面的设置初始化器一样,也是使用SpringFactoriesLoader从META-INF/spring.factories文件获取所有可用的监听器类ApplicationListener
  4. this.mainApplicationClass = this.deduceMainApplicationClass(),用于推断并设置项目main()方法启动的主程序启动类

WebApplicationType.deduceFromClasspath();

		static WebApplicationType deduceFromClasspath() {
        // WebApplicationType.REACTIVE 类型  通过类加载器判断REACTIVE相关的Class是否存在
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) // 判断REACTIVE相关的Class是否存在
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		// WebApplicationType.NONE 类型
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) { // 不存在 Servlet 的类
				return WebApplicationType.NONE;
			}
		}
		// WebApplicationType.SERVLET 类型。可以这样的判断的原因是,引入 Spring MVC 时,是内嵌的 Web 应用,会引入 Servlet 类。
		return WebApplicationType.SERVLET;
	}

getSpringFactoriesInstances(ApplicationContextInitializer.class)

/**
     * 获得指定类类对应的对象们。
     *
     * @param type 指定类
     * @param <T> 泛型
     * @return 对象们
	 * 这里的入参type是:org.springframework.context.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
        // 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组  
		// type = org.springframework.context.ApplicationContextInitializer.class
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
		//org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
		// 根据names来进行实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 对实例进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

    /**
     * 创建对象的数组
     *
     * @param type 父类
     * @param parameterTypes 构造方法的参数类型
     * @param classLoader 类加载器
     * @param args 参数
     * @param names 类名的数组
     * @param <T> 泛型
     * @return 对象的数组
     */
	@SuppressWarnings("unchecked")
	// parameterTypes: 上一步得到的names集合
	private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<>(names.size()); // 数组大小,细节~
		// 遍历 names 数组
		for (String name : names) {
			try {
			    // 获得 name 对应的类
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				// 确认被加载类是ApplicationContextInitializer的子类
				Assert.isAssignable(type, instanceClass);
				// 获得构造方法
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				// 创建对象
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				//加入实例列表中
				instances.add(instance);
			} catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

getSpringFactoriesInstances(ApplicationListener.class), 同上,只有入参不一样

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 主要用于简单统计 run 启动过程的时长。
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 headless 属性
		configureHeadlessProperty();


		//   (1)获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 创建  ApplicationArguments 对象 初始化默认应用参数类
			// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			//(2)项目运行环境Environment的预配置
			// 创建并配置当前SpringBoot应用将要使用的Environment
			// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

			configureIgnoreBeanInfo(environment);
			// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
			Banner printedBanner = printBanner(environment);

			// (3)创建Spring容器
			context = createApplicationContext();
			// 获得异常报告器 SpringBootExceptionReporter 数组
			//这一步的逻辑和实例化初始化器和监听器的一样,
			// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);


			// (4)Spring容器前置处理
			//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

			// (5):刷新容器
			refreshContext(context);

			// (6):Spring容器后置处理
			//扩展接口,设计模式中的模板方法,默认为空实现。
			// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
			afterRefresh(context, applicationArguments);
			// 停止 StopWatch 统计时长
			stopWatch.stop();
			// 打印 Spring Boot 启动的时长日志。
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// (7)发出结束执行的事件通知
			listeners.started(context);

			// (8):执行Runners
			//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
			//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
			//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
		    // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

        //   (9)发布应用上下文就绪事件
		//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
		// 这样整个Spring Boot项目就正式启动完成了。
		try {
			listeners.running(context);
		} catch (Throwable ex) {
            // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
            handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		 //返回容器
		return context;
	}
  • 从上述代码中可以看出总共有9个步骤
  1. getRunListeners
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 这里仍然利用了getSpringFactoriesInstances方法来获取实例,
		// 从META-INF/spring.factories中读取Key为org.springframework.boot.SpringApplicationRunListener的Values
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

在这里插入图片描述
2. prepareEnvironment

	/**
	 * 加载外部化配置资源到environment,包括命令行参数、servletConfigInitParams、
	 * 	servletContextInitParams、systemProperties、sytemEnvironment、random、
	 * 	application.yml(.yaml/.xml/.properties)等;初始化日志系统。
	 * @param listeners
	 * @param applicationArguments
	 * @return
	 */
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {

		//获取或创建环境(存在就直接返回,不存在创建一个再返回)
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境:配置PropertySources和active Profiles
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。
		listeners.environmentPrepared(environment);
		//将环境绑定到SpringApplication
		bindToSpringApplication(environment);
		//如果非web环境,将环境转换成StandardEnvironment
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// 配置PropertySources对它自己的递归依赖
		// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
	
	/**
     * @return 获得或创建 ConfigurableEnvironment 对象
     */
	private ConfigurableEnvironment getOrCreateEnvironment() {
	    // 已经存在,则进行返回
		if (this.environment != null) {
			return this.environment;
		}
		// 不存在,则根据 webApplicationType 类型,进行创建。
		switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
		}
	}
  1. createApplicationContext
	protected ConfigurableApplicationContext createApplicationContext() {
	    // 根据 webApplicationType 类型,获得 ApplicationContext 类型
		// 这里创建容器的类型 还是根据webApplicationType进行判断的,
		// 该类型为SERVLET类型,所以会通过反射装载对应的字节码,
		// 也就是AnnotationConfigServletWebServerApplicationContext

		// 先判断有没有指定的实现类
		Class<?> contextClass = this.applicationContextClass;
		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);
			}
		}
		// 创建 ApplicationContext 对象
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
/**
	 * The class name of application context that will be used by default for non-web
	 * environments.
	 */
	public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

	/**
	 * The class name of application context that will be used by default for web
	 * environments.
	 */
	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

	/**
	 * The class name of application context that will be used by default for reactive web
	 * environments.
	 */
	public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

通过上述代码可以看出创建的是AnnotationConfigServletWebServerApplicationContext

  1. prepareContext
	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//设置容器环境,包括各种变量
	    context.setEnvironment(environment);

		//设置上下文的 bean 生成器和资源加载器
		postProcessApplicationContext(context);

		//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
		applyInitializers(context);

		//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
		listeners.contextPrepared(context);

		//记录启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		// 加载所有资源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
		load(context, sources.toArray(new Object[0]));

		//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
        listeners.contextLoaded(context);

		//这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等
	}
  1. refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
	    // 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
		refresh(context);
		// 注册 ShutdownHook 钩子
		if (this.registerShutdownHook) {
			try {
				//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
				context.registerShutdownHook();
			} catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
	/**
	 * Refresh the underlying {@link ApplicationContext}.
	 * @param applicationContext the application context to refresh
	 */
	protected void refresh(ApplicationContext applicationContext) {
	    // 断言,判断 applicationContext 是 AbstractApplicationContext 的子类
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		// 启动(刷新) AbstractApplicationContext
		((AbstractApplicationContext) applicationContext).refresh();
	}

从上面创建ApplicationContext过程可以知道refresh执行的是AnnotationConfigServletWebServerApplicationContext中的方法,代码如下

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
  1. afterRefresh,空方法,自己扩展
	/**
	 * Called after the context has been refreshed.
	 * @param context the application context
	 * @param args the application arguments
	 */
	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
		//该方法没有实现,可以根据需要做一些定制化的操作。
	}
  1. listeners.started
public void started(ConfigurableApplicationContext context) {
		//执行所有SpringApplicationRunListener实现的started方法。
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}
  1. callRunners

Spring Boot提供的执行器接口有ApplicationRunner 和CommandLineRunner两种,在使用时只需要自定义一个执行器实现其中一个接口并重写对应的run()方法,然后Spring Boot项目启动后会立即执行这些方法

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	    // 获得所有 Runner 们
		List<Object> runners = new ArrayList<>();
		// 获得所有 ApplicationRunner 实现类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// 获得所有 CommandLineRunner 实现类
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		// 排序 runners
		AnnotationAwareOrderComparator.sort(runners);
		// 遍历 Runner 数组,执行逻辑
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}
	
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args);
		} catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

从上面的代码可以看出 ApplicationRunner优先CommandLineRunner执行

  1. listeners.running
public void running(ConfigurableApplicationContext context) {
		//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}
@Override
public void running(ConfigurableApplicationContext context) {
	context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}

整体流程大概如下

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值