提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
SpringBoot是基于Spring框架的,只是简化了Spring 项目的搭建,SpringBoot项目启动非常简单调用SpringApplication
的run方法就可以启动一个SpringBoot项目
接下来我们来看下SpringBoot的启动流程
一、SpringApplication
SpringApplication.run(DemoApplication.class, args)
先调用静态方法创建一个SpringApplication对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
- 构造方法创建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));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
第4行是run方法传入的类,注意这个类上有@SpringBootApplication
注解
第5行设置应用的类型有 NONE
SERVLET
REACTIVE
三种
第6行读取spring.factories文件的Bootstrapper
接口实现类设置到SpringApplication容器
第7行读取spring.factories文件的ApplicationContextInitializer
接口实现类设置到SpringApplication容器
第8行读取spring.factories文件的ApplicationListener
接口实现类设置到SpringApplication容器
二、SpringApplication run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
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;
}
- 第3行 创建一个StopWatch 对象,这个是Spring的一个工具类,可方便的对程序部分代码进行ms级别的计时
- 第5行 创建一个DefaultBootstrapContext 对象(这个类里面提供了一些注册方法),我们管它叫SpringBoot容器类吧,并初始化
Bootstrapper
实现类
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
- 第7行创建一个
SpringBootExceptionReporter
集合,后面会读取spring.factories配置文件 SpringBootExceptionReporter 实现类 - 第8行设置
java.awt.headless
系统变量 - 第10-11行 拿到spring.factories配置文件
SpringApplicationRunListener
实现类(SpringBoot中只有EventPublishingRunListener
一个实现类,这个类主要在SpringBoot启动过程中发布事件) 并且启动,发布ApplicationStartingEvent
事件,EventPublishingRunListener创建过程中,拿到了SpringApplication 中的监听器,也就是这里发布的事件只有spring.factories文件配置的ApplicationListener
接口实现类才能监听到
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
- 第13创建 ApplicationArguments (应用程序启动参数)
- 第15-16行准备环境配置信息
ConfigurableEnvironment
,同时发布ApplicationEnvironmentPreparedEvent
事件,EnvironmentPostProcessorApplicationListener
监听器在监听到ApplicationEnvironmentPreparedEvent事件后,会调用EnvironmentPostProcessor
接口的postProcessEnvironment,来处理环境信息,在其它的SpringCloud组件中的spring.factories也有定义EnvironmentPostProcessor扩展 - 第18行打印启动日志
- 第20-21行创建一个Spring容器
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
如果是Servlet环境就是AnnotationConfigServletWebServerApplicationContext
这个类内部定义了 AnnotatedBeanDefinitionReader
ClassPathBeanDefinitionScanner
两个解析器,都是用来注册Bean到容器的
- 获取spring.factories配置文件 SpringBootExceptionReporter 实现类,前面创建的Spring容器context作为参数
- 第25行prepareContext我们具体来看看源码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//前面准备好的环境信息设置添加到创建好的Spring容器
context.setEnvironment(environment);
postProcessApplicationContext(context);
//调用spring.factories 配置的ApplicationContextInitializer initialize方法,入参是Spring容器
applyInitializers(context);
// 发布ApplicationContextInitializedEvent事件,spring.factories文件配置`的ApplicationListener`接口实现类才能监听到
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
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 对于 SpringApplication.run(DemoApplication.class, args) 这里source就是DemoApplication,注意这个类上有@SpringBootApplication注解
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载bean到容器
load(context, sources.toArray(new Object[0]));
// 发布ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
- 第26行
refreshContext
刷新下Spring容器,上一步完成了bean的注册,这里再刷新容器 - 第27行
afterRefresh
定义了个钩子 - 第32行
listeners.started(context)
发布ApplicationStartedEvent事件
SpringBootApplication注解
SpringBoot项目启动类上都会加上SpringBootApplication
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
这里有三个注解 @SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中@SpringBootConfiguration注解又有@Configuration
注解信息,
前面调用SpringApplication
run方法创建AnnotationConfigServletWebServerApplicationContext容器时,向Spring容器添加了一个AnnotatedBeanDefinitionReader ,这里添加了ConfigurationClassPostProcessor后处理器,然后再容器refresh过程中就会处理@SpringBootConfiguration
修饰的主类信息了
1 ComponentScan注解
这个主要是指定扫描路径,spring会把指定路径下带有指定注解的类注册到IOC容器中,basePackages、value:指定扫描路径,如果为空则以@ComponentScan注解的类所在的包为基本的扫描路径
2 EnableAutoConfiguration
自动导入接口,可以看到这里注解通过Import注解又导入了个AutoConfigurationImportSelector
类,同时又有个@AutoConfigurationPackage
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
这个注解导入了一个Registrar
类 实现了ImportBeanDefinitionRegistrar
接口, ConfigurationClassPostProcessor对于Import的类如果实现这个接口,调用registerBeanDefinitions
方法,这里往容器中注册了一个Bean beanClass 是BasePackages
类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
AutoConfigurationImportSelector
我们知道EnableAutoConfiguration 导入的这个类能够可以扫描spring.factories
文件中的EnableAutoConfiguration接口实现类,从而将复合条件的类注册到Spring容器
具体是怎么做到呢,这个需要了解下ConfigurationClassPostProcessor
怎么处理Import导入的类,AutoConfigurationImportSelector的继承体系同时实现了ImportSelector
DeferredImportSelector
两个接口
AutoConfigurationImportSelector 实现了DeferredImportSelector接口,ConfigurationClassPostProcessor在处理这个类时调用到了ConfigurationClassParser内部类DeferredImportSelectorHandler
的process方法
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
// DeferredImportSelectorGroupingHandler
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
// DeferredImportSelectorGroupingHandler
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
/**
*ImportSelector 接口类selectImports获取的值包装成成一个Entry 集合
*/
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
先register 再调用processGroupImports,上面的register方法拿到了一个Group对象,这在AutoConfigurationImportSelector就是AutoConfigurationGroup
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
processGroupImports 方法调用 grouping.getImports()返回的是一个存有Group.Entry对象的迭代器,交由具体的group(AutoConfigurationGroup)去执行process selectImports 方法
// AutoConfigurationGroup
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//getAutoConfigurationEntry 这个方法获取到了spring.factories的 EnableAutoConfiguration 实现类
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
// AutoConfigurationGroup
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
selectImports 就收集EnableAutoConfiguration 实现类包装成一个Entry对象交给Spring 的ConfigurationClassParser 注册到容器当中
总结
这里简单整理了下SpringBoot的启动过程,主要了解SpringApplication run方法怎样创建Spring容器,还有就是AutoConfigurationImportSelector自动注入Bean的原理,这一点需要结合Spring中的ConfigurationClassPostProcessor(处理Configuration注解)容器后处理器来看
另外发现一个奇怪的地方
AutoConfigurationImportSelector 中有一个直接实现ImportSelector接口的selectImports在SpringBoot项目启动过程中实际是没有调用到,这个不知道什么原因
AutoConfigurationImportSelector 入口方法是getImportGroup