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中的配置类会作为一个根路径由上到下去扫描组件

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

接下来我们逐行分析一下:

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

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

执行start方法设置开始时间

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

初始化异常类空集合

4configureHeadlessProperty();

设置Headless模式【true/false】

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

5SpringApplicationRunListeners listeners = getRunListeners(args);

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

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

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

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

创建应用参数实体

初始化了CommandLineArgs

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

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

集合

commandLineArgs.addOptionArg(optionName, optionValue);

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

commandLineArgs.addNonOptionArg(arg);

8ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);

准备环境参数配置

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

SERVLET:  new StandardServletEnvironment(); 

REACTIVE:  new StandardReactiveWebEnvironment(); 

default:  new StandardEnvironment();

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

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

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

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

SERVLET:       
AnnotationConfigServletWebServerApplicationContext
REACTIVE: AnnotationConfigReactiveWebServerApplicationContext
default: 
AnnotationConfigApplicationContext
12

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

context);

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

准备应用上下文

为context设置环境参数

为context设置beanNameGenerator,resourceLoader,classloader

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

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

获取启动类sources

调用BeanDefinitionLoader的load方法注册bean

调用listeners的contextLoaded方法

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

刷新的后续处理

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

基本spring程序已经正常工作

16stopWatch.stop();

结束执行状态记录器

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

17

new StartupInfoLogger(this.mainApplicationClass)

    .logStarted(getApplicationLog(), stopWatch);
}

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

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

  • ApplicationRunner
  • CommandLineRunner

执行相应的run方法

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

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值