SpringBoot2.1.1启动流程 - 启动你的springboot

二、启动你的springboot

2.1 启动springboot有两种方式:

(1)SpringApplication.run(springboot配置注解类.class,args)

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

 如果你希望添加一些自己的配置,那么也可以用引用.set的方式构建启动类,但是这会使代码不简洁

@SpringBootApplication
public class Ep1Application {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(new HashSet<String>(Collections.singleton(Ep1Application.class.getName())));
        springApplication.setBannerMode(Banner.Mode.CONSOLE); 
        springApplication.setWebApplicationType(WebApplicationType.NONE);
        springApplication.setAdditionalProfiles("prod");
        springApplication.setHeadless(true);
        springApplication.run();
    }
}

(2)new SpringApplicationBuilder --使用builder设计模式,使代码看起来更简洁

@SpringBootApplication
public class Ep1Application {
    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .sources(Ep1Application.class)//SpringBootApplication配置类
                .bannerMode(Banner.Mode.CONSOLE)//banner 也就是启动logo
                .web(WebApplicationType.NONE)//web类型,【SERVLET、REACTIVE、NONE】
                .profiles("prod") //配置类型【开发为dev,测试为test,生产为prod】
                .headless(true) //运行 headless 服务器,来进行简单的图像处理
                .run(args);
    }
}

                .sources(Ep1Application.class)   //   SpringBootApplication配置类
                .bannerMode(Banner.Mode.CONSOLE)    //   banner 也就是启动logo
                .web(WebApplicationType.NONE)    //   web类型,【SERVLET、REACTIVE、NONE】
                .profiles("prod")     //   配置类型【开发为dev,测试为test,生产为prod】
                .headless(true)    //   运行 headless 服务器,来进行简单的图像处理

重点是:启动springboot直接运行run方法即可,因为spring约定大于配置,使用默认的即可快速启动一个springboot工程。

2.2 为什么run方法要获取当前注解@SpringBootApplication类的class

目的是以当前的class路径去扫描其下的各种组件;我们举一个例子来说明

说明:aaa包下有a组件类,重写了toString方法,bbb包下有b组件类,重写了toString方法。两类都有@Component注解

aaa包下建立包含@SpringBootApplication注解的TestApplication类作为Springboot source的参数。

当我在启动的时候同时a类可以正常加载 b类没有被加载。由此也说明source中的配置类会作为一个根路径由上到下去扫描组件

扫描二维码关注公众号,回复: 4997077 查看本文章

SpringApplication可以从不同的来源读取bean,一般是使用带有@Configuration注解的类来引导,同时也可以使用

  • 由AnnotatedBeanDefinitionReader加载的完全限定类名
  • 由XmlBeanDefinitionReader加载的XML资源或由GroovyBeanDefinitionReader加载的groovy脚本的位置
  • 由ClassPathBeanDefinitionScanner扫描的包的名称

因为我们的Ep1Application带有@SpringBootApplication注解,而这个注解又继承自@Configuration注解,所以应用当前类来加载是没问题的,你也可以自己定义一个带有@SpringBootApplication的类去加载。

2.3 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 stopWatch = new StopWatch();
stopWatch.start();

一个执行状态记录器,主要记录了一些启动时监控参数

执行start方法设置开始时间

2 ConfigurableApplicationContext context = null;      创建应用环境变量的引用
3 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

初始化异常类空集合

4 configureHeadlessProperty();

设置Headless模式【true/false】

Headless模式是在缺少显示屏、键盘或者鼠标是的系统配置。在java.awt.toolkit和java.awt.graphicsenvironment类中有许多方法,除了对字体、图形和打印的操作外还可以调用显示器、键盘和鼠标的方法。但是有一些类中,比如Canvas和Panel,可以在headless模式下执行。

5 SpringApplicationRunListeners listeners = getRunListeners(args);

使用工厂模式创建springboot运行时监听器。

工厂就是指从META-INF/spring.factorie读取需要加载的监听器。

然后使用组合模式,迭代读取到的监听器。

6 listeners.starting(); starting()方法开启监听器。
7 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

创建应用参数实体

初始化了CommandLineArgs

然后调用parser方法解析设置的命令参数

如果有设置命令,执行下述方法,将参数放入map

集合

commandLineArgs.addOptionArg(optionName, optionValue);

否则执行下述方法将命令放入下述list集合

commandLineArgs.addNonOptionArg(arg);

8 ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);

准备环境参数配置

一、获取web类型,生成对应的环境实体

SERVLET:  new StandardServletEnvironment(); 

REACTIVE:  new StandardReactiveWebEnvironment(); 

default:  new StandardEnvironment();

二、set环境参数到map集合中

三、发出通知,环境已经准备好了

9 configureIgnoreBeanInfo(environment); 设置环境变量 spring.beaninfo.ignore = true
10 Banner printedBanner = printBanner(environment); 打印banner
11 context = createApplicationContext();

根据web推断类型创建应用上下文

SERVLET:       
AnnotationConfigServletWebServerApplicationContext
REACTIVE: AnnotationConfigReactiveWebServerApplicationContext
default: 
AnnotationConfigApplicationContext
12

exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class },

context);

加载异常分析器到上下文中
13 prepareContext(context, environment, listeners, applicationArguments,printedBanner);

准备应用上下文

为context设置环境参数

为context设置beanNameGenerator,resourceLoader,classloader

递归所有待初始化的初始化器

 调用他们的初始化方法,注册单例对象

获取启动类sources

调用BeanDefinitionLoader的load方法注册bean

调用listeners的contextLoaded方法

14 refreshContext(context);
主要执行下列步骤
准备刷新
prepareRefresh();
初始化beanfactory
prepareBeanFactory(beanFactory);
丰富类注解
postProcessBeanFactory(beanFactory);
激活beanfactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
注册bean的初始化逻辑处理器
registerBeanPostProcessors(beanFactory);
国际化
initMessageSource();
初始化应用事件广播
initApplicationEventMulticaster();
留给子类继承扩展使用
onRefresh();
注册到消息广播器中
registerListeners();
完成刷新过程
finishBeanFactoryInitialization(beanFactory);
清除缓存
finishRefresh();
15 afterRefresh(context, applicationArguments);

刷新的后续处理

执行finish方法,获取最终的ApplicationReadyEvent事件。

基本spring程序已经正常工作

16 stopWatch.stop();

结束执行状态记录器

设置结束时间、总共执行时间、最后一个执行任务等信息

17

new StartupInfoLogger(this.mainApplicationClass)

    .logStarted(getApplicationLog(), stopWatch);
}

打印服务启动日志
18 listeners.started(context); EventPublishRunningListener 启动
19 callRunners(context, applicationArguments);

从context中获取所有的runner,根据runner的 类型

  • ApplicationRunner
  • CommandLineRunner

执行相应的run方法

20 listeners.running(context); context发布事件:ApplicationReadyEvent
21 return context; 返回上下文

猜你喜欢

转载自blog.csdn.net/qq_31615049/article/details/85528328
今日推荐