SpringBoot启动流程
启动原理
public static void main(String[] args) {
//xxx.class:主配置类,(可以传多个)
SpringApplication.run(xxx.class, args);
}Copy to clipboardErrorCopied
-
从run方法开始,创建SpringApplication,然后再调用run方法
/** * ConfigurableApplicationContext(可配置的应用程序上下文) */ public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { //调用下面的run方法 return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }Copy to clipboardErrorCopied
-
创建SpringApplication
//primarySources:主配置类 new SpringApplication(primarySources)Copy to clipboardErrorCopied
public SpringApplication(Class<?>... primarySources) { //调用下面构造方法 this((ResourceLoader) 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.lazyInitialization = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //保存主配置类 this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); //获取当前应用的类型,是不是web应用,见2.1 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来,见2.2 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); //从类路径下找到META‐INF/spring.ApplicationListener;然后保存起来,原理同上 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有main方法的主配置类,见下图(在调run方法的时候是可以传递多个配置类的) this.mainApplicationClass = this.deduceMainApplicationClass(); //执行完毕,SpringApplication对象就创建出来了,返回到1处,调用SpringApplication对象的run方法,到3 }Copy to clipboardErrorCopied
2.1 判断是不是web 应用 2.2 getSpringFactoriesInstances(ApplicationContextInitializer.class) -
调用SpringApplication对象的run方法
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); //声明IOC容器 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); //从类路径下META‐INF/spring.factories获取SpringApplicationRunListeners,原理同2中获取ApplicationContextInitializer和ApplicationListener SpringApplicationRunListeners listeners = this.getRunListeners(args); //遍历上一步获取的所有SpringApplicationRunListener,调用其starting方法 listeners.starting(); Collection exceptionReporters; try { //封装命令行 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备环境,把上面获取到的listeners传过去,见3.1 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); //打印Banner,就是控制台那个Spring字符画 Banner printedBanner = this.printBanner(environment); //根据当前环境利用反射创建IOC容器 context = this.createApplicationContext(); //从类路径下META‐INF/spring.factories获取SpringBootExceptionReporter,原理同2中获取ApplicationContextInitializer和ApplicationListener exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //准备IOC容器,见3.3 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新IOC容器,可查看配置嵌入式Servlet容器原理 链接在3.4 this.refreshContext(context); //这是一个空方法 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } //调用所有SpringApplicationRunListener的started方法 listeners.started(context); //见3.5 ,从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调ApplicationRunner先回调,CommandLineRunner再 this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { //调用所有SpringApplicationRunListener的running方法 listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }Copy to clipboardErrorCopied
//容器创建完成,返回步骤1处,最后返回到启动类
3.1 3.2 3.3 3.4 3.5
几个重要的事件回调机制
配置在META-INF/spring.factories
- ApplicationContextInitializer
- SpringApplicationRunListener
只需要放在ioc容器中
- ApplicationRunner
- CommandLineRunner
测试
-
创建
ApplicationContextInitializer
和SpringApplicationRunListener
的实现类,并在META-INF/spring.factories文件中配置public class TestApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { System.out.println("TestApplicationContextInitializer.initialize"); } }Copy to clipboardErrorCopied
public class TestSpringApplicationRunListener implements SpringApplicationRunListener { @Override public void starting() { System.out.println("TestSpringApplicationRunListener.starting"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { System.out.println("TestSpringApplicationRunListener.environmentPrepared"); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("TestSpringApplicationRunListener.contextPrepared"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("TestSpringApplicationRunListener.contextLoaded"); } @Override public void started(ConfigurableApplicationContext context) { System.out.println("TestSpringApplicationRunListener.started"); } @Override public void running(ConfigurableApplicationContext context) { System.out.println("TestSpringApplicationRunListener.running"); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("TestSpringApplicationRunListener.failed"); } }Copy to clipboardErrorCopied
org.springframework.context.ApplicationContextInitializer=\ cn.clboy.springbootprocess.init.TestApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener=\ cn.clboy.springbootprocess.init.TestSpringApplicationRunListenerCopy to clipboardErrorCopied
启动报错:说是没有找到带org.springframework.boot.SpringApplication和String数组类型参数的构造器,给TestSpringApplicationRunListener添加这样的构造器
public TestSpringApplicationRunListener(SpringApplication application,String[] args) { }Copy to clipboardErrorCopied
-
创建
ApplicationRunner
实现类和CommandLineRunner
实现类,注入到容器中@Component public class TestApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("TestApplicationRunner.run\t--->"+args); } }Copy to clipboardErrorCopied
@Component public class TestCommandLineRunn implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("TestCommandLineRunn.runt\t--->"+ Arrays.toString(args)); } }Copy to clipboardErrorCopied
修改Banner
默认是找类路径下的banner.txt
,可以在配置文件中修改
spring.banner.location=xxx.txtCopy to clipboardErrorCopied
生成banner的网站:http://patorjk.com/software/taag
也可以使用图片(将其像素解析转换成assii编码之后打印),默认是在类路径下找名为banner
后缀为"gif", "jpg", "png"
的图片
static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};Copy to clipboardErrorCopied
也可以在配置文件中指定
spring.banner.image.location=classpath:abc.png