spring boot 项目主类启动入口
@SpringBootApplication //<1>
public class SrcSpringBootExampleTsshareApplication {
public static void main(String[] args) {
//<2>
SpringApplication.run(SrcSpringBootExampleTsshareApplication.class, args);
}
}
//<1>
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class>[0], args);
}
//<2>
public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
return run(new Class>[] { primarySource }, args);
}
//<3>
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
//<4>
public static int exit(ApplicationContext context, ExitCodeGenerator... exitCodeGenerators) {
Assert.notNull(context, "Context must not be null");
int exitCode = 0;
try {
try {
ExitCodeGenerators generators = new ExitCodeGenerators();
Collection beans = context.getBeansOfType(ExitCodeGenerator.class).values();
generators.addAll(exitCodeGenerators);
generators.addAll(beans);
exitCode = generators.getExitCode();
if (exitCode != 0) {
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
}
finally {
close(context);
}
}
catch (Exception ex) {
ex.printStackTrace();
exitCode = (exitCode != 0) ? exitCode : 1;
}
return exitCode;
}
public SpringApplication(Class>... primarySources) {
this(null, 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)
*/
@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));
//通过 classpath ,判断 Web 应用类型。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
META-INF/spring.factories
下ApplicationContextInitializer 初始化实例META-INF/spring.factories
下ApplicationListener 初始化实例private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// <1> 排序对象们加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// <2>创建对象们
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// <3>创建对象们
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
# 省略代码
public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 通过类加载器获取资源 "META-INF/spring.factories" 下对应的map 资源
Enumeration urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
#run(String.. arg) 运行 Spring 应用。代码如下:
/**
* 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}
* > 1. 配置属性
* > 2。设置系统属性『java.awt.headless』
* > 3. 获取监听器,发布应用开始启动事件
* > 4. 初始化输入参数
* > 5. 配置环境,
* > 6. 输出 banner
* > 7. 创建上下文
* > 8. 实例化异常分析器
* > 9. 预处理上下文
* > 10. 刷新上下文
* > 11. 再一次刷新上下文
* > 12. 停止 StopWatch 统计时长
* > 13. 打印 Spring Boot 启动的时长日志
* > 14. 发布应用已经启动的事件
* > 15. 遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法
* > 16. 通知 SpringApplicationRunListener 的数组,Spring 容器运行中
*/
public ConfigurableApplicationContext run(String... args) {
//<1> 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
/**<2> 设置系统属性『java.awt.headless』,为true则启用headless模式支持**/
configureHeadlessProperty();
/***<3>
* 通过SpringFactoriesLoader 加载 *META-INF/spring.factories
* 找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
* /之后逐个调用其started()方法,广播SpringBoot要开始执行了
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//<4>初始化参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//<5>创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//<6>打印banner
Banner printedBanner = printBanner(environment);
//<7>创建应用上下文 spring 容器
context = createApplicationContext();
//<8> 通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//<9> 为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
//主要是调用所有初始化类的 initialize 方法
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//<10> 刷新上下文 启动spring 容器
refreshContext(context);
//<11> 再一次刷新上下文( 执行 Spring 容器的初始化的后置逻辑。默认实现为空) ,其实是空方法,可能是为了后续扩展。 。
afterRefresh(context, applicationArguments);
//<12>停止 StopWatch 统计时长
stopWatch.stop();
//<13> 打印 Spring Boot 启动的时长日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//<14> 发布应用已经启动的事件 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成
listeners.started(context);
//<15> 遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//<16>通知 SpringApplicationRunListener 的数组,Spring 容器运行中
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
以下是针对上面run 方法中一些复杂方法的单独拆分分析
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//<1> 设置context的environment 属性
context.setEnvironment(environment);
//<2> 设置context 一些属性
postProcessApplicationContext(context);
//<3> 初始化 ApplicationContextInitializer
applyInitializers(context);
//<4> 告诉SpringApplicationRunListener 监听器上下文初始化完毕
listeners.contextPrepared(context);
// <5> 打印日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// <6> 设置 beanFactory 的属性
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// <7> 加载 BeanDefinition 们
Set
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
根据懒加载的属性来设置懒加载的beanFactoryPostProcessor配置
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// <1> 创建 BeanDefinitionLoader 对象
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// <2> 设置 loader 的属性
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// <3> 执行 BeanDefinition 加载
loader.load();
}
<1> 处,调用 #getBeanDefinitionRegistry(ApplicationContext context) 方法,创建 BeanDefinitionRegistry 对象
关于 BeanDefinitionRegistry 类,暂时不需要深入了解。感兴趣的胖友,可以看看 《【死磕 Spring】—— IoC 之 BeanDefinition 注册表:BeanDefinitionRegistry》 文章。
*<8> #contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成
private void refreshContext(ConfigurableApplicationContext context) {
//<1> 开启(刷新)Spring 容器
refresh(context);
//<2> 注册ShutdownHook 钩子
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
<1> 调用 AbstractApplicationContext# refresh(context);
这个方法为启动容器最为核心的方法,处理的内容比较多,这里也会触发可以触发 Spring Boot 的自动配置的功能,包括tomcat的实例的创建和启动等 下面分析回着重细致讲解。
<2> ConfigurableApplicationContext#registerShutdownHook() 方法,注册 ShutdownHook 钩子。这个钩子,主要用于 Spring 应用的关闭时,销毁相应的 Bean 们
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// <1> 获得所有 Runner 们 ApplicationRunner & CommandLineRunner
List
参考文档:http://svip.iocoder.cn/Spring-Boot/SpringApplication ,芋艿源码–只是星球进行梳理。大家一起学习和进步。