赞
踩
- public ConfigurableApplicationContext run(String... args) {
- // 创建一个StopWatch实例,用来记录SpringBoot的启动时间
- StopWatch stopWatch = new StopWatch();
- stopWatch.start();
- ConfigurableApplicationContext context = null;
- Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
- configureHeadlessProperty();
- // 通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
- SpringApplicationRunListeners listeners = getRunListeners(args);
- // 发布SprintBoot启动事件:ApplicationStartingEvent
- listeners.starting();
- try {
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
- // 创建和配置environment,发布事件:SpringApplicationRunListeners#environmentPrepared
- ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
- configureIgnoreBeanInfo(environment);
- // 打印SpringBoot的banner和版本
- Banner printedBanner = printBanner(environment);
- // 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
- context = createApplicationContext();
- exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
- new Class[] { ConfigurableApplicationContext.class }, context);
- // 准备ApplicationContext,Initializers设置到ApplicationContext后发布事件:ApplicationContextInitializedEvent
- // 打印启动日志,打印profile信息(如dev, test, prod)
- // 调用EventPublishingRunListener发布ApplicationContext加载完毕事件:ApplicationPreparedEvent
- prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- // 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作
- // 以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
- refreshContext(context);
- // hook方法
- afterRefresh(context, applicationArguments);
- // stopWatch停止计时,日志打印总共启动的时间
- stopWatch.stop();
- if (this.logStartupInfo) {
- new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
- }
- // 发布SpringBoot程序已启动事件ApplicationStartedEvent
- listeners.started(context);
- // 调用ApplicationRunner和CommandLineRunner
- callRunners(context, applicationArguments);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, listeners);
- throw new IllegalStateException(ex);
- }
-
- try {
- // 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
- listeners.running(context);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, null);
- throw new IllegalStateException(ex);
- }
- return context;
- }
-
Springboot 启动过程,其核心就是IOC容器初始化过程,所以可以把上诉过程简化为
IOC容器是由类AnnotationConfigServletWebServerApplicationContext初始化的一个实例,其中包含了bean工厂、类加载器、bean定义信息和环境变量等主要信息。
通过AnnotationConfigServletWebServerApplicationContext的初始化,IOC容器初始化了一些属性,设置了一些特定的bean定义信息。其主要的作用,还是为后续的IOC容器创建Bean做准备。
在项目启动后,进行一系列的初始化。包括SpringApplication实例准备,listener监听器准备,environment环境准备。
加载当前主启动类下的所有类文件到IOC容器:经过ConfigurationClassParser类中的parse方法调用AnnotatedBeanDefinition类型的parse方法,我们完成了对当前项目工程下所有类,注册到IOC容器中的操作。
加入Spring自带的框架类到IOC容器(自动装配):在ConfigurationClassParser类中的执行方法为:this.deferredImportSelectorHandler.process(); 这里用到Spring Factories 机制,见Springboot自动配置
关于bean信息注册的方法,基本都在ConfigurationClassParser这个类中,processConfigBeanDefinitions实现bean信息注册的入口方法在ConfigurationClassParser的parse方法中:
- public void parse(Set<BeanDefinitionHolder> configCandidates) {
- //主启动类bean信息集合
- Iterator var2 = configCandidates.iterator();
- while(var2.hasNext()) {
- BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
- BeanDefinition bd = holder.getBeanDefinition();
- try {
- if (bd instanceof AnnotatedBeanDefinition) {
- //注册项目工程类信息
- this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
- .......
- }
- //注册Spring提供默认类信息
- this.deferredImportSelectorHandler.process();
- }
然后,就可以完成bean的大规模实例化
这是一个抽象方法,且没有继承类。
记录结束时间;
发布SpringBoot程序已启动事件;
执行容器中所有的ApplicationRunner和CommandLineRunner类型的run方法
- public ConfigurableApplicationContext run(String... args) {
- ......
- ConfigurableApplicationContext context = null;
- ......
- //1创建容器
- context = this.createApplicationContext();
- exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
- //2准备容器相关信息
- this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- //3刷新容器,也就是通常我们说的bean的初始化
- this.refreshContext(context);
- //4完成初始化后处理
- this.afterRefresh(context, applicationArguments);
- ......
- }
自定义初始化器首先要实现 ApplicationContextInitializer 接口
执行自定义初始化器的方式有三种
- @SpringBootApplication
- public class NettyTestApplication {
-
- public static void main(String[] args) {
- SpringApplication springApplication = new SpringApplication(NettyTestApplication.class);
- springApplication.addInitializers(new SecondInitializer());
- springApplication.run(args);
- }
- }
-
- @Order(2)
- public class SecondInitializer implements ApplicationContextInitializer {
-
- @Override
- public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
- ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
-
- Map<String, Object> map = new HashMap<>();
- map.put("key2","value2");
-
- MapPropertySource firstInitializer = new MapPropertySource("secondInitializer", map);
- environment.getPropertySources().addLast(firstInitializer);
-
- System.out.println("run second initializer");
- }
- }
- # 在 resources 文件夹下新建 META-INF 文件夹,META-INF 文件夹下新建 spring.factories 配置文件,添加内容如下:
- org.springframework.context.ApplicationContextInitializer=com.xxx.FirstInitializer
-
-
-
- // order 注解用于排序
- @Order(1)
- public class FirstInitializer implements ApplicationContextInitializer {
-
- @Override
- public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
- ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
-
- Map<String, Object> map = new HashMap<>();
- map.put("key1","value1");
-
- MapPropertySource firstInitializer = new MapPropertySource("firstInitializer", map);
- environment.getPropertySources().addLast(firstInitializer);
-
- System.out.println("run first initializer");
- }
- }
- 在 springboot 的默认配置文件 application.properties 中添加
- context.initializer.classes=com.xxx.ThirdInitializer
-
- @Order(3)
- public class ThirdInitializer implements ApplicationContextInitializer {
-
- @Override
- public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
- ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
-
- Map<String, Object> map = new HashMap<>();
- map.put("key3","value3");
-
- MapPropertySource firstInitializer = new MapPropertySource("thirdInitializer", map);
- environment.getPropertySources().addLast(firstInitializer);
-
- System.out.println("run third initializer");
- }
- }
-
- @SpringBootApplication
- public class NettyTestApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(NettyTestApplication.class, args);
- }
-
- }
springboot进行事件监听有四种方式:
1.手工向ApplicationContext中添加监听器
- public class MyListener1 implements ApplicationListener<MyEvent>{
- Logger logger = Logger.getLogger(MyListener1.class);
-
- public void onApplicationEvent(MyEvent event)
- {
- logger.info(String.format("%s监听到事件源:%s.", MyListener1.class.getName(), event.getSource()));
- }
- }
-
-
- @SpringBootApplication
- public class LisenterApplication
- {
- public static void main(String[] args)
- {
- ConfigurableApplicationContext context = SpringApplication.run(LisenterApplication.class, args);
- //装载监听
- context.addApplicationListener(new MyListener1());
- }
- }
2.使用@Component注解将监听器装载入spring容器
- @Component
- public class MyListener2 implements ApplicationListener<MyEvent>
- {
- Logger logger = Logger.getLogger(MyListener2.class);
-
- public void onApplicationEvent(MyEvent event)
- {
- logger.info(String.format("%s监听到事件源:%s.", MyListener2.class.getName(), event.getSource()));
- }
- }
3.在application.properties中配置监听器
- 在application.properties中配置监听
- context.listener.classes=com.listener.MyListener3
-
- public class MyListener3 implements ApplicationListener<MyEvent>
- {
- Logger logger = Logger.getLogger(MyListener3.class);
-
- public void onApplicationEvent(MyEvent event)
- {
- logger.info(String.format("%s监听到事件源:%s.", MyListener3.class.getName(), event.getSource()));
- }
- }
4.通过@EventListener注解实现事件监听
- @Component
- public class MyListener4
- {
- Logger logger = Logger.getLogger(MyListener4.class);
-
- @EventListener
- public void listener(MyEvent event)
- {
- logger.info(String.format("%s监听到事件源:%s.", MyListener4.class.getName(), event.getSource()));
- }
- }
在启动 Spring Boot 项目时会在控制台打印如下内容(logo 和版本信息):
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
- ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v2.6.6)
如果我们想要使用自己的banner,我们可以直接替换,在项目的 resources 资源目录下创建 banner.txt 文件,banner.txt 内容如下
- // _ooOoo_ //
- // o8888888o //
- // 88" . "88 //
- // (| ^_^ |) //
- // O\ = /O //
- // ____/`---'\____ //
- // .' \\| |// `. //
- // / \\||| : |||// \ //
- // / _||||| -:- |||||- \ //
- // | | \\\ - /// | | //
- // | \_| ''\---/'' | | //
- // \ .-\__ `-` ___/-. / //
- // ___`. .' /--.--\ `. . ___ //
- // ."" '< `.___\_<|>_/___.' >'"". //
- // | | : `- \`.;`\ _ /`;.`/ - ` : | | //
- // \ \ `-. \_ __\ /__ _/ .-` / / //
- // ========`-.____`-.___\_____/___.-`____.-'======== //
- // `=---=' //
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
- // 佛祖保佑 永无BUG 永不修改 //
- :: Spring Boot :: (${spring-boot.version})
SpringBoot在项目启动时如果遇到异常并不能友好的打印出具体的堆栈错误信息,我们只能查看到简单的错误消息,以致于并不能及时解决发生的问题,针对这个问题SpringBoot提供了故障分析仪的概念(failure-analyzer),内部根据不同类型的异常提供了一些实现。
实现方法是,在springboot启动的核心方法run中会加载所有的SpringBootExceptionReporter,SpringBootExceptionReporter是一个回调接口,用于支持对SpringApplication启动错误的自定义报告。里面就一个报告启动失败的方法。
其实现类:org.springframework.boot.diagnostics.FailureAnalyzers
用于触发从spring.factories加载的FailureAnalyzer和FailureAnalysisReporter实例。
SpringBoot内部通过实现AbstractFailureAnalyzer抽象类定义了一系列的针对性异常类型的启动分析,如下图所示:
SpringBoot内部提供的启动异常分析都是指定具体的异常类型实现的,最常见的一个错误就是端口号被占用(PortInUseException),虽然SpringBoot内部提供一个这个异常的启动分析,我们也是可以进行替换这一异常分析的,我们只需要创建PortInUseException异常的AbstractFailureAnalyzer,并且实现类注册给SpringBoot即可,实现自定义如下所示:
- /**
- * 端口号被占用{@link PortInUseException}异常启动分析
- */
- public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
- /**
- * logger instance
- */
- static Logger logger = LoggerFactory.getLogger(PortInUseFailureAnalyzer.class);
-
- @Override
- protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
- logger.error("端口被占用。", cause);
- return new FailureAnalysis("端口号:" + cause.getPort() + "被占用", "PortInUseException", rootFailure);
- }
- }
在上面我们只是编写了指定异常启动分析,我们接下来需要让它生效,这个生效方式比较特殊,类似于自定义SpringBoot Starter AutoConfiguration的形式,我们需要在META-INF/spring.factories文件内进行定义,如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer
项目启动遇到的异常顺序不能确定,很可能在Spring IOC并未执行初始化之前就出现了异常,我们不能通过@Component注解的形式使其生效,所以SpringBoot提供了通过spring.factories配置文件的方式定义。
在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等。可以通过实现Runner接口完成以上工作。
1.实现 ApplicationRunner 接口
- @Component
- public class VipSoftServerRunner implements ApplicationRunner{
-
- private static final Logger logger = LoggerFactory.getLogger(JmxhphServerRunner.class);
-
- @Override
- public void run(ApplicationArguments args) throws Exception {
- System.out.println("项目启动,执行 CommandLineRunner 实现类的方法");
- }
- }
2.实现 CommandLineRunner 接口
- @Component
- public class VipSoftServerRunner implements CommandLineRunner {
-
- private static final Logger logger = LoggerFactory.getLogger(JmxhphServerRunner.class);
-
- @Override
- public void run(String... args) throws Exception {
- System.out.println("项目启动,执行 CommandLineRunner 实现类的方法");
- }
- }
两者只是参数上的区别而已。
选项参数的有效示例:
- --foo
- --foo=
- --foo=""
- --foo=bar
- --foo="bar then baz"
- --foo=bar,baz,biz
-
- java -jar test.jar --server.port=8080
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。