Spring boot启动原理及相关组件_springboot 组件
优质博文:IT-BLOG-CN
一、Spring Boot应用启动
一个Spring Boot应用的启动通常如下:
@SpringBootApplication@Slf4jpublic class ApplicationMain { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args); }}
执行如上代码,Spring Boot程序启动成功。事实上启动Spring Boot应用离不开SpringApplication。
所以,我们跟随SpringApplication的脚步,开始从源码角度分析Spring Boot的初始化过程。
btw,可参看例子一节,我对Spring Boot启动的拓展点都做了demo,可参照下面源码分析进行理解。
文档有一句话说了SpringApplication做了什么(目的):
Create an appropriate ApplicationContext instance (depending on your classpath) Register a CommandLinePropertySource to expose command line arguments as Spring properties Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans
二、SpringApplication构造函数
启动代码先创建SpringApplication示例,在执行run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
如下是SpringApplication的构造函数代码分析。
this.resourceLoader = resourceLoader;Assert.notNull(primarySources, \"PrimarySources must not be null\");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//通过Classloader探测不同web应用核心类是否存在,进而设置web应用类型this.webApplicationType = WebApplicationType.deduceFromClasspath(); //找出所有spring.factories中声明的ApplicationContextInitializer并设置,//ApplicationContextInitializer定义了回调接口,在refresh()前初始化调用(即在prepareContext的applyInitializers方法中调用)setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//找出所有spring.factories中声明的ApplicationListener(细节往后再叙),ApplicationListener继承了//java.util.EventListener,实现了类似观察者模式的形式,通过实现ApplicationListener、SmartApplicationListener,能够监听Spring上下文的refresh、Prepared等事件或者是自定义事件setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//找出主启动类(有趣的是,是通过new一个runtime异常然后在异常栈里面找出来的)this.mainApplicationClass = deduceMainApplicationClass();
在构造期间,主要做了:
1、判定应用类型,为后面创建不同类型的spring context做准备。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出启动类。
三、run()源码解析
介绍run()方法前,先说说贯穿run方法的ApplicationRunListener,它有助于理解整个run()的运行周期。
写在这里:Spring Application事件机制
run()方法分析如下:
//java.awt.headless,是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。configureHeadlessProperty();//将spring.factories中的SpringApplicationRunListener接口实现类拖出来,塞到SpringApplicationRunListeners(一个集合)中,统一批量执行SpringApplicationRunListeners listeners = getRunListeners(args);//触发runlistener的startinglisteners.starting();try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //spring.beaninfo.ignore如果没有设置值,则把它设为true,具体情况具体设置, //如果没用的话,把它设为true可以ignore掉classloader对于不存在的BeanInfo的扫描,提高性能。 configureIgnoreBeanInfo(environment); //banner打印。自定义banner挺好玩的 Banner printedBanner = printBanner(environment); //根据webApplicationType(一开始推断的应用类型)去新建applicationContext context = createApplicationContext(); //获取SpringBootExceptionReporter,回调接口类,提供启动时的异常报告 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //下面会说 prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); //do nothing afterRefresh(context, applicationArguments); //计时停止 stopWatch.stop(); //打日志 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //启动 listeners.started(context); //找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并执行 callRunners(context, applicationArguments);
下面再分别说说两个方法(prepareEnvironment、refreshContext)的代码。
四、prepareEnvironment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); //发布environment prepared事件 listeners.environmentPrepared(