理清主线再深入细节
main方法解析
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//分成两部分:1.生成应用对象 2.执行run方法
return new SpringApplication(primarySources).run(args);
}
new SpringApplication(primarySources)部分
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
加载细节就只解读到这个层次,后续研究类加载器文件加载时,可以以这部分为参考来学习。
这部分细节涉及的关键点主要是两个,一个是类加载器,一个是缓存。对于后续如果要解读整体的缓存方案,这部分也需要了解。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//1.从META/spring.facteries文件中读取预定义的ApplicationContextInitializer的相关实现全路径类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//2.把读取到的类实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//3.根据javax.annotation.Priority的值排序(值大的优先级高)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
最基础的Spring-Boot-Web的项目,
默认加载的ApplicationContextInitializer为:(排序后)
0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer" 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer" 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer" 3 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer" 4 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer" 5 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer" 6 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
默认加载的ApplicationListener为:(排序后)
0 = "org.springframework.boot.ClearCachesApplicationListener" 1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener" 2 = "org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor" 3 = "org.springframework.boot.context.FileEncodingApplicationListener" 4 = "org.springframework.boot.context.config.AnsiOutputApplicationListener" 5 = "org.springframework.boot.context.config.ConfigFileApplicationListener" 6 = "org.springframework.boot.context.config.DelegatingApplicationListener" 7 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener" 8 = "org.springframework.boot.context.logging.LoggingApplicationListener" 9 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener" 10 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"
默认执行到这里的堆栈为:
0 = {StackTraceElement@1968} "org.springframework.boot.SpringApplication.deduceMainApplicationClass(SpringApplication.java:279)" 1 = {StackTraceElement@1969} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:274)" 2 = {StackTraceElement@1970} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:253)" 3 = {StackTraceElement@1971} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)" 4 = {StackTraceElement@1972} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)" 5 = {StackTraceElement@1973} "com.example.demo.DemoApplication.main(DemoApplication.java:10)"
SpringApplication.run()方法部分
主体代码为:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//启动一个用于计时的StopWatch
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//声明上下文容器
ConfigurableApplicationContext context = null;
//声明异常报告容器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置java.awt.headless为SpringApplicationheadless属性值,默认为true
configureHeadlessProperty();
//从META/spring.facteries中读取SpringApplicationRunListener并实例化,封装成一个SpringApplicationRunListeners对象,该对象与SpringApplication共用一个logger
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动运行时监听器,此处只有org.springframework.boot.context.event.EventPublishingRunListener一个监听
//这个监听做的事情如下:
//1.新建一个org.springframework.boot.context.event.ApplicationStartingEvent事件
//2.广播该事件(广播的原理是,拿到所有注册对于该事件感兴趣的监听,用taskExecutor执行)
//那么问题来了,是在哪里注册的事件关注呢(追了一下源码,监听器本身常量定义了关注的事件,是在getApplicationListeners的过程中,扫描所有监听器,通过代理方法判断是否关注,然后缓存起来的)
listeners.starting();
try {
//声明入参对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境,做的事情主要如下:(参见下面的解读)
//组建一个环境对象
//加载各种Converter、Formatter、数据读取源、profile;
//这个过程中会广播一个ApplicationEnvironmentPreparedEvent事件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//如果环境属性spring.beaninfo.ignore为空,则设置一个环境属性spring.beaninfo.ignore=true
configureIgnoreBeanInfo(environment);
//打印Banner信息
//SpringApplication.bannerMode=OFF时可以关闭Banner打印
//通过属性spring.banner.image.location读取Banner的图像信息,支持gif/jpg/png三个格式,找banner.gif、banner.jpg、banner.png
//文字版找spring.banner.location下的banner.txt
//图片和文字都找不到时以SpringBootBanner作为默认的banner
//找到多个Banner时会都打印
//打印完Banner后,会通过SpringBootVersion.getVersion()打印版本信息(此处是带颜色的打印)
Banner printedBanner = printBanner(environment);
//SERVLET应用,默认创建一个org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext容器,该容器创建时会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
context = createApplicationContext();
//从MEAT/spring.factories中加载SpringBootExceptionReporter的相关配置并创建实例
//默认加载的只有一个 org.springframework.boot.diagnostics.FailureAnalyzers
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//初始化context容器对象(塞各种属性),
//找到之前加载的ApplicationContextInitializer,执行initialize方法 !!!
//并发布一个ApplicationContextInitializedEvent事件
//由logStartupInfo开关控制,打印一条开始的info日志,如果开启了debug级别还会有一条debug的运行日志;打印激活的profile
//注册一个springApplicationArguments的单例bean
//printedBanner如果不为空,也注册为单例bean
//如果是默认的bean工程DefaultListableBeanFactory,设置由allowBeanDefinitionOverriding属性控制是否允许重载
//由lazyInitialization控制加载一个LazyInitializationBeanFactoryPostProcessor处理器
//创建BeanDefinitionLoader,执行其load方法
//load方法里,以Competent的方式注册入口类(这部分其他内容没有看出来意图是什么)
//load完毕后,创建并发布一个ApplicationPreparedEvent事件
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//这部分具体看后面关于refresh的解析 !!!
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();