Spring Boot 1.5x启动全过程源码分析(下)

目录

1.创建并启动计时监控类

4.获取并启动监听器

5、根据运行监听器和应用参数来准备 Spring 环境

6.创建容器

7.准备应用上下文

8.刷新应用上下文

9.应用上下文刷新后置处理

10.发布应用上下文启动完成事件

11.停止计时监控

13.返回上下文

总结


本文是对Springboot1.5x源码中的run方法和相关源码进行分析。

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}
	public ConfigurableApplicationContext run(String... args) {
        //1.设置时间监控
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
        //2.初始化应用上下文配置
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
        //3.java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
		configureHeadlessProperty();
        //获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
		//4:获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //5:构造容器环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            //打印banner
			Banner printedBanner = printBanner(environment);
            //6:创建容器
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
            //7:准备应用上下文
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            //8:刷新应用上下文
			refreshContext(context);
            //9:应用上下文刷新后置处理
			afterRefresh(context, applicationArguments);
            //10:发布应用上下文启动完成事件
			listeners.finished(context, null);
            //11.停止计时监控类
			stopWatch.stop();
            //12.输出日志记录执行主类名、时间信息
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            //13.返回上下文
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

1.创建并启动计时监控类

StopWatch stopWatch = new StopWatch();
stopWatch.start();

关于StopWatch的相关源码

    public void start() throws IllegalStateException {
		start("");
	}

	public void start(String taskName) throws IllegalStateException {
		if (this.running) {
			throw new IllegalStateException("Can't start StopWatch: it's already running");
		}
		this.running = true;
		this.currentTaskName = taskName;
		this.startTimeMillis = System.currentTimeMillis();
	}

首先记录了当前任务的名称,默认为空字符串,然后记录当前 Spring Boot 应用启动的开始时间。

4.获取并启动监听器

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

创建逻辑和之前实例化初始化器和监听器的一样,一样调用的是 getSpringFactoriesInstances 方法来获取配置的监听器名称并实例化所有的类。

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

SpringApplicationRunListeners(Log log,
        Collection<? extends SpringApplicationRunListener> listeners) {
    this.log = log;
    this.listeners = new ArrayList<>(listeners);
}

SpringApplicationRunListener 所有监听器配置在 spring-boot-2.0.3.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面。

5、根据运行监听器和应用参数来准备 Spring 环境

ConfigurableEnvironment environment = prepareEnvironment(listeners,
        applicationArguments);
configureIgnoreBeanInfo(environment);

5.1关于prepartEnvironment的的源码如下

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

5.2 配置应用环境

protected void configureEnvironment(ConfigurableEnvironment environment,
        String[] args) {
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
}

分两种方式配置,一种是property sources;第二种是Profiles

6.创建容器

创建应用上下文

context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

其实就是根据不同的应用类型初始化不同的上下文应用类。

7.准备应用上下文

prepareContext(context, environment, listeners, applicationArguments,
        printedBanner);


private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
        //绑定环境到上下文
		context.setEnvironment(environment);
        //配置上下文的 bean 生成器及资源加载器
		postProcessApplicationContext(context);
        //为上下文应用所有初始化器
		applyInitializers(context);
        //触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
		listeners.contextPrepared(context);
        //记录启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// 注册两个特殊的单例bean
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// 加载所有资源
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));
        //触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
		listeners.contextLoaded(context);
	}

8.刷新应用上下文

refreshContext(context);

这个主要是刷新 Spring 的应用上下文,刷新容器

9.应用上下文刷新后置处理

下面是关于afterRefresh的相关源码和方法

afterRefresh(context, applicationArguments);

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
		callRunners(context, args);
	}


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

ApplicationRunner、CommandLineRunner这两个接口会在容器启动后进行调用,相当于开机启动,可以实现多个。

根据类型的到bean,根据order排序,循环调用接口的run()方法。

10.发布应用上下文启动完成事件

 

listeners.finished(context, null);

public void finished(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
            //遍历当前的监听器,并触发回调当前这个监听器的方法
			callFinishedListener(listener, context, exception);
		}
	}

11.停止计时监控

stopWatch.stop();

public void stop() throws IllegalStateException {
    if (this.currentTaskName == null) {
        throw new IllegalStateException("Can't stop StopWatch: it's not running");
    }
    long lastTime = System.currentTimeMillis() - this.startTimeMillis;
    this.totalTimeMillis += lastTime;
    this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
    if (this.keepTaskList) {
        this.taskList.add(this.lastTaskInfo);
    }
    ++this.taskCount;
    this.currentTaskName = null;
}
```java
计时监听器停止,并统计一些任务执行信息。

**12 输出日志记录执行主类名、时间信息**
```java
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
}

13.返回上下文

return context;

总结

springboot1.5x和springboot2.x的启动过程大同小异,在创建容器的过程中没有太大的区别,在run方法中,最大的不同就是在启动容器的方式和流程。如有雷同,纯属正常。

Run方法中的区别

1.5x2.0x
listeners.finished(context, null);

 

listeners.started(context);
 callRunners(context, applicationArguments);

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值