从 java main 方法启动
SpringApplication 构造方法
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType(); // 推断 web 应用类型
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class)); // 获取 spring 工厂实例
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
getSpringFactoriesInstances 源码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 获取指定类型的工厂名字
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, // 根据名字、类型创建工厂实例
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
从源码我们看出主要做了三件事:
- loadFactoryNames,加载指定类型的工厂名称
loadSpringFactories:
a. 查找类路径下全部的META-INF/spring.factories的URL
b. 根据url加载全部的spring.factories中的属性
c. 将所有spring.factories中的值缓存到 SpringFactoriesLoader 的 cache 中,方便下次调用 - createSpringFactoriesInstances,创建指定类型的工厂实例,根据上面获取的指定类型的工厂名称列表来实例化工厂 bean,我们可以简单的认为通过反射来实例化
- 对工厂实例进行排序,然后返回排序后的实例列表
构造总结:
- 构造自身实例
- 推测 web 应用类型,并赋值到属性 webApplicationType
- 设置属性 initializers 和 listeners 中途读取了类路径下所有 META-INF/spring.factories 的属性,并缓存到了 SpringFactoriesLoader 的 cache 缓存中
- 推断主类,并赋值到属性 mainApplicationClass
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 = new StopWatch();
stopWatch.start();
// spring应用上下文,也就是我们所说的spring根容器
ConfigurableApplicationContext context = null;
// 自定义SpringApplication启动错误的回调接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置jdk系统属性java.awt.headless,默认情况为true即开启
configureHeadlessProperty();
// KEY 1 - 获取启动时监听器
SpringApplicationRunListeners listeners = getRunListeners(args)
// 触发启动事件,启动监听器会被调用,一共5个监听器被调用
listeners.starting();
try {
// 参数封装,也就是在命令行下启动应用带的参数,如--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// KEY 2 - 准备环境 1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中;默认为true即开启
configureIgnoreBeanInfo(environment);
// 打印banner图
Banner printedBanner = printBanner(environment);
// KEY 3 - 创建应用上下文,并实例化了其三个属性:reader、scanner和beanFactory
context = createApplicationContext();
// 获取异常报道器,即加载spring.factories中的SpringBootExceptionReporter实现类
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// KEY 4 - 准备上下文前置处理
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// KEY 5 - Spring上下文刷新
refreshContext(context);
// KEY 6 - Spring上下文后置处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
getRunListeners() 方法:返回一个新的SpringApplicationRunListeners实例对象
细看的话,这次是从SpringFactoriesLoader的cache中取SpringApplicationRunListener类型的类(全限定名),然后实例化后返回。说的简单点,getRunListeners就是准备好了运行时监听器EventPublishingRunListener。
listeners.starting() 方法:构建了一个ApplicationStartingEvent事件,并将其发布出去,对每个listener进行invokeListener,调用过滤出的监听器
prepareEnvironment() 方法:
// 准备环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment 创建和配置环境
// 获取或创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境:配置PropertySources和activeProfiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
// listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)
listeners.environmentPrepared(environment);
// 将环境绑定到SpringApplication
bindToSpringApplication(environment);
// 如果是非web环境,将环境转换成StandardEnvironment
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
// 配置PropertySources对它自己的递归依赖
ConfigurationPropertySources.attach(environment);
return environment;
}
configureEnvironment() 方法:将配置任务按顺序委托给 configurePropertySources 和 configureProfiles
configurePropertySources:注释说明是增加、移除或者重排序应用环境中的 PropertySource。就目前而言,如果有命令行参数则新增封装命令行参数的PropertySource,并将它放到sources的第一位置。
configureProfiles:配置应用环境中的哪些配置文件处于激活状态(或默认激活)。可以通过spring.profiles.active属性在配置文件处理期间激活其他配置文件。说的简单点就是设置哪些Profiles是激活的。
listeners.environmentPrepared(environment):这其中会初始化 PropertiesPropertySourceLoader 和 YamlPropertySourceLoader 这两个加载器从 file:./config/,file:./,classpath:/config/,classpath:/ 路径下加载配置文件,PropertiesPropertySourceLoader 加载配置文件 application.xml 和application.properties,YamlPropertySourceLoader 加载配置文件 application.yml 和application.yaml。目前我们之后 classpath:/ 路径下有个 application.yml 配置文件,将其属性配置封装进了一个名叫 applicationConfig:[classpath:/application.yml] 的 OriginTrackedMapPropertySource 中,并将此对象放到了 propertySourceList 的最后。
environmentPrepared 方法会触发所有监听了 ApplicationEnvironmentPreparedEvent 事件的监听器,加载外部化配置资源到 environment,包括命令行参数、servletConfigInitParams、servletContextInitParams、systemProperties、sytemEnvironment、random、application.yml(.yaml/.xml/.properties) 等;
bindToSpringApplication(environment):就是将 environment 绑定到 SpringApplication
createApplicationContext:根据SpringApplication的webApplicationType来实例化对应的上下文,对其部分属性:reader、scanner、beanFactory进行了实例化;reader中实例化了属性conditionEvaluator;scanner中添加了两个AnnotationTypeFilter:一个针对@Component,一个针对@ManagedBean;beanFactory中注册了8个注解配置处理器。
prepareContext:
1、将 context 中的 environment 替换成 SpringApplication 中创建的 environment
2、将SpringApplication中的 initializers 应用到 context 中
设置application id,并将application id封装成ContextId对象,注册到beanFactory中
向context的beanFactoryPostProcessors中注册了一个ConfigurationWarningsPostProcessor实例
向context的applicationListeners中注册了一个ServerPortInfoApplicationContextInitializer实例
向context的beanFactoryPostProcessors中注册了一个CachingMetadataReaderFactoryPostProcessor 实例
向context的applicationListeners中注册了一个ConditionEvaluationReportListener实例
3、加载两个单例bean到beanFactory中
向beanFactory中注册了一个名叫springApplicationArguments的单例bean,该bean封装了我们的命令行参数;
向beanFactory中注册了一个名叫springBootBanner的单例bean。
4、加载bean定义资源
5、将 SpringApplication 中的 listeners 注册到 context 中,并广播 ApplicationPreparedEvent 事件
总共11个 ApplicationListener 注册到了 context 的 applicationListeners 中;
ApplicationPreparedEvent 事件的监听器一共做了两件事:
+ 向 context 的 beanFactoryPostProcessors 中注册了一个 PropertySourceOrderingPostProcessor 实例
+ 向 beanFactory 中注册了一个名叫 springBootLoggingSystem 的单例 bean,也就是我们的日志系统 bean
context 中主要是三个属性增加了内容:beanFactory、beanFactoryPostProcessors 和 applicationListeners
load:就是加载 bean 定义资源,支持4种方式:Class、Resource、Package和CharSequence。
Class:注解形式的 Bean 定义;AnnotatedBeanDefinitionReader 负责处理。
Resource:一般而言指的是 xml bean 配置文件,也就是我们在 spring 中常用的 xml 配置。说的简单点就是:将 xml 的 bean 定义封装成 BeanDefinition 并注册到 beanFactory 的 BeanDefinitionMap 中;XmlBeanDefinitionReader 负责处理。
Package:以扫包的方式扫描bean定义; ClassPathBeanDefinitionScanner 负责处理。
CharSequence:以先后顺序进行匹配 Class、Resource 或 Package 进行加载,谁匹配上了就用谁的处理方式处理。