提起微服务,SpringBoot是我们经常要谈的一个话题。SpringBoot的搭建过程非常简单,几分钟就可以搞定一个小demo。启动类也很简单。
一共两步。
@SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
这个小demo里只有一行逻辑,SpringApplication.run。如果要看启动流程,那肯定就是看这个里面了。跟源码之前,最好搞一份源码debug。看懂一部分了,标注一下注意点,或者写一下你的感想。(源码编译,可以参考这篇文章:SpringBoot源码编译,感谢这位老哥的分享)
下面开始跟源码
进入run方法,最开始看到的是这行代码
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这行代码有2块逻辑。一块是:new SpringApplication,另外一块是:run()
先看new SpringApplication的逻辑,重要逻辑我都加了注释
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//记录web服务的类型,默认是servlet类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载SpringBoot初始化器,初始化器的配置在META-INF/spring.factories
//ApplicationContextInitializer只有一个方法:initialize(C applicationContext);我们可以看到方法的入参是applicationContext。
//所以,ApplicationContextInitializer的作用是我们可以创建自己的ApplicationContextInitializer,从而对Spring容器做一些操作。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载springboot启动的listener。listener的配置同样在META-INF/spring.factories中
//这些listener根据不同的事件会做出不同的操作。(SpringBoot的启动过程中,在不同的阶段有不同的事件产生)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//springboot的启动方法必须是main方法
this.mainApplicationClass = deduceMainApplicationClass();
}
小结一下。这部分的逻辑,主要就是将Initializer以及listener从spring.factories加载进内存中。
再看run的逻辑,重要逻辑我加了注释
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取事件发布器publisher,springboot默认配置的是EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//1、发布ApplicationStartingEvent事件,内部关注这个事件的listener会执行相应的逻辑
listeners.starting();
try {
//包装初始化运行参数,比如:我们启动SpringBoot的jar包时,启动命令上可以加参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2、这一步会发布ApplicationEnvironmentPreparedEvent事件。进行环境准备工作,比如:生成Environment对象,并将启动参数设置进Environment。加载spring.profiles.active配置等。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//加载spring.beaninfo.ignore的配置,这个属性的配置意思是:像BeanFactory、ApplicationContext对象可以通过BeanFactoryAware以及ApplicationContextAware获取到,不需要Autowire方式注入
configureIgnoreBeanInfo(environment);
//3、自定义banner输出。可以自定义SpringBoot启动的输出,配图片或者字符都可以
Banner printedBanner = printBanner(environment);
//4、创建AnnotationConfigServletWebServerApplicationContext类,这是容器的上下文对象。
//AnnotationConfigServletWebServerApplicationContext类做了两件事
// 第一件:将处理Configuration注解的processor、处理Autowired注解的processor注册到容器中
//第二件:加载classpath下的resource资源文件类加载器
context = createApplicationContext();
//5、一个callback,可以处理SpringBoot启动过程中的错误
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//6、初始化spring容器对象,默认是DefaultListableBeanFactory。做业务的时候,我们和这个对象打交道的时候最多。
//加载SpringBean,这个时候的bean是BeanDefinition对象
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新Spring上下文对象。这一步主要是向spring上下文中注册BeanPostProcessor以及BeanFactoryPostProcessor,事件广播,注册监听器
//这一步,还有一个很重要的事。将所有的beanDefintion创建成真正可使用的bean
refreshContext(context);
//Spring上下文对象刷新后的一个扩展点
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布ApplicationStartedEvent事件
listeners.started(context);
//执行ApplicationRunner、CommandLineRunner的逻辑
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;
}
小结一下。这个run逻辑是SpringBoot启动的核心逻辑,比较重要的几个点是:
1、执行listener,初始化日志、控制台颜色、代理listener等模块。这块逻辑会发布ApplicationStartingEvent事件
2、创建Environment对象,加载application.yml或者application.properties文件的相关配置
3、打印banner,banner支持图片和文字
4、创建Spring容器上下文对象AnnotationConfigServletWebServerApplicationContext,这是我们业务逻辑要打交道的对象。这块逻辑会发布ApplicationPreparedEvent事件
5、SpringBootExceptionReporter逻辑处理
6、准备Spring上下文,这一步的主要作用是:执行ApplicationContextInitializer,这个接口方法是ApplicationContext,所以可以对Spring上下文进一步处理。另外一个很重要的逻辑是扫描程序包或者resources包,获取到bean的定义,生成beanDefinition对象
7、刷新Spring上下文。给Spring上下文注册BeanPostProcessor以及listener,另外有一个很重要的逻辑是:将上一步生成的beanDefintion变成真正可用的bean对象。
8、容器执行扩展点
9、再次执行listener。这块逻辑发布ApplicationStartedEvent事件
10、最后执行ApplicatinoRunner、CommandLineRunner的逻辑
SpringBoot的源码深度整合了Spring。其中各种和Spring框架进行交互。代码写的很清晰,值得深入研究一下。SpringBoot的源码研究透彻,感觉对Spring的使用也会有一个新的体会。这一篇先聊这么多。后面再补充。。。。