Spring Boot 启动分析
启动分析那么之间找它的入口,我们在启动Spring Boot时候会指定一个Class,而这个Class一般都会带有@SpringBootApplication注解。
@SpringBootApplication
标识一个Configuration类-Spring boot启动配置(和@Configuration类一样特性),并且还触发自动装配(auto-configuration)和组件扫描(component scanning),等于@Configuration、@EnableAutoConfiguratio、 @ComponentScan 。实际上它是一个混合注解。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//排除加载的类
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
//排除加载的类名
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
//扫描的包名
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//扫描的类名
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
//bean名称生成器
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
//是否代理@Bean方法
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
ps:@Configuration和@Component的区别?
@Configuration默认情况下为@Bean方法生代理,以拦截的对应的方法,而@Component仅仅标识为一个Spring Bean。
1、@EnableAutoConfiguration
可以看到Import一个AutoConfigurationImportSelector和@AutoConfigurationPackage
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
1.1、@AutoConfigurationPackage-自动装配扫包
自动配置所需要扫描的包,可以看到导入了AutoConfigurationPackages.Registrar,它实现了**ImportBeanDefinitionRegistrar接口(配置类解析时),也是说在BeanFactoryPostProcessor(允许对BeanDefinition进行修改和增加)中加载配置类的BeanDefinition时,会执行registerBeanDefinitions方法。根据源码,它会根据注解配置扫描对应的包并注册**BeanDefinition。
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
//配置扫描包名
String[] basePackages() default {};
//配置扫描class所在的包
Class<?>[] basePackageClasses() default {};
}
//扫描包并注册
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));
}
}
1.1.1、自动配置具体扫描的包
默认情况下扫描**org.springframework.boot.autoconfigure**包下的class,具体扫包情况如下:
@AutoConfigurationPackage配置的basePackages@AutoConfigurationPackage配置的basePackageClasses所对应的包- 如果上面两个为空,则扫描注解所在的包
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>();
for (String basePackage : attributes.getStringArray("basePackages")) {
packageNames.add(basePackage);
}
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
//为空即默认情况下扫描注解在包
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
1.2、ImportSelector
自动装配注解还导入了AutoConfigurationImportSelector,它实现了DeferredImportSelector,那么在Import时也就是配置类解析的时候执行selectImports返回需要注册的类名,并递归解析返回的类名,即最终解析了selectImports返回的所有配置类。
通过源码我们可以看到具体是getAutoConfigurationEntry去获得需要自动装配的配置。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
//省略其他字段
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//判断是开启自动装配
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获得自动装配的Entry
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
//
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//1、spi读取所有候选Class
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//2、去重
configurations = removeDuplicates(configurations);
//3、排除配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//4、条件装配
configurations = getConfigurationClassFilter().filter(configurations);
//5、触发import事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//省略其他
}
(1)SPI机制读取候选的自动装配类名
在getCandidateConfigurations中,通过**SpringFactoriesLoader也就是SPI**机制读取META-INF/spring.factories文件中key=org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的Class Name;
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
//返回自动装配读取的key所对应的类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
//根据key=全类名,SPI机制读取文件中对应配置类 即此时key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//SpringFactoriesLoader 所读取的资源路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
(2)去重
通过LinkedHashSet去重
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
(3)排除配置的exclusions
获得注解中设置的需要排除的类名,并检查是否存在需要排除Class是否存在,不存在即为无效,存在无效抛出异常。最后排除掉配置的exclusions
//获得注解设置的需要排除的类名
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//检查排除的配置是否在存在,即所以的排除类需要存在
checkExcludedClasses(configurations, exclusions);
//删除这些类
configurations.removeAll(exclusions);
(4)过滤
一些资源可能需要一些前置资源,例如Mybaits就就需要DataSource所以Spring Boot提供了条件装配机制。在ConfigurationClassFilter中根据**AutoConfigurationImportFilter以及autoConfigurationMetadata自动装配元数据对候选的class进行过滤,也就是根据@Conditional和spring-autoconfigure-metadata.properties中配置装配条件挑出能够装配**的Class Name。
Conditional
SPI机制加载AutoConfigurationImportFilter对候选的Class进行Conditional过滤。例如OnBeanCondition,OnClassCondition等这些条件配置过滤。针对不同维度分为以下三种Conditional
# Auto Configuration Import Filters 自动装配过滤
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
//SPI机制加载`AutoConfigurationImportFilter`
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
//创建ConfigurationClassFilter
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
//SPI机制加载`AutoConfigurationImportFilter`
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter`.class, this.beanClassLoader);
}
-
OnBeanCondition用于检查特定的bean是否存在,对应以下注解:
-
@ConditionalOnBean当指定类的Bean存在时,进行装配
-
``@ConditionalOnMissingBean`
当指定类的Bean不存在时,进行装配
-
@ConditionalOnSingleCandidate这些注解当指定类的Bean存在且为单例,进行装配
-
-
OnClassCondition和
OnBeanCondition类似,不过针对是Class 是否存在,对应以下注解:-
@ConditionalOnClass当指定类存在时,进行装配
-
``@ConditionalOnMissingClass`
当指定类不存在时,进行装配
-
-
OnWebApplicationCondition用于检查是否在Web容器中,对应以下注解
-
@ConditionalOnWebApplication当SpringApplication是Web容器时
-
``@ConditionalOnNotWebApplication`
当SpringApplication不是Web容器时
-
-
其他Condition
spring-autoconfigure-metadata
因为提供很多Starter不同的Starter,所以它也提供了这些自动的条件,根据源码可以看到读取了META-INF/spring-autoconfigure-metadata.properties这一配置文件,里面配置了各个类的装配的条件。
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path): ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
//摘取的配置元数据片段
//表示CacheAutoConfiguration必须在CacheAspectSupport这个bean存在时才生效
xxx.CacheAutoConfiguration.ConditionalOnBean=xxx.CacheAspectSupport
//表示CacheAutoConfiguration必须在CacheManager这个类存在时才生效
xxx.CacheAutoConfiguration.ConditionalOnClass=xxx.CacheManager
(5)发表自动装配导入事件
SPI机制加载AutoConfigurationImportListener,并触发自动装配导入事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//SPI机制加载AutoConfigurationImportListener
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
//触发监听器的import事件
listener.onAutoConfigurationImportEvent(event);
}
}
}
2、 @ComponentScan-组件扫描
在ConfigurationClassPostProcessor这一BeanFactory后处理器中对配置类进行解析(ConfigurationClassParser),而在配置类的解析过程中如果发现配置类中存在@ComponentScan则根据这个注解配置(ComponentScanAnnotationParser解析注解)进行扫包(ClassPathBeanDefinitionScanner进行扫包),对每个扫描出来的BeanDefinition当做配置类解析。
ClassPathBeanDefinitionScanner.doScan默认情况下扫描以下三种注解标识的类:
-
@Component
Spring 提供的标识一个Bean
-
@ManagedBean
JSR-250
-
@Named
JSR-330
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//遍历所有的包路径
for (String basePackage : basePackages) {
//获得所有的候选 即扫描Class path下的 class
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
//通过过滤器来判断哪些是符合候选的
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
//注册默认过滤器
protected void registerDefaultFilters() {
//@Component是候选的
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//@ManagedBean 是候选的
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//@Named是候选的
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
3、自动装配是在什么时候生效的
上文可以是自动装配和组件扫描其实就是利用@Import注册了AutoConfigurationImportSelector和AutoConfigurationImportSelector那是怎么生效的呢?
答案:在ConfigurationClassPostProcessor中(Spring 刷新流程中的postBeanFactorPostProcessor-允许我们对已有BeanDefinition做修改或者增加),它会递归解析配置类对应些@Import、ImportSelector接口、@Bean方法等等都会去解析并注册到Spring中,此时不就生效了么。其实大多数需要扫描都是通过BeanFactorPostProcessor或者已有BeanFactorPostProcessor提供的功能实现。例如@MapperScan
//配置类解析代码
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations 读取@PropertySource指定的配置文件并到这个
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations 组件扫描注册
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations @Import注解 包含ImportBeanDefinitionRegistrar
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations @ImportResource 导入的xml配置
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods @Bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
启动流程
SpringApplication.run(primarySource, args)为Spring boot 启动的入口
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1、创建SpringApplication
//2、调用SpringApplication.run(args)方法
return new SpringApplication(primarySources).run(args);
}
1、实例化-SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//需要在spring 刷新先注册的字段
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载ApplicationContextInitializer用于初始化上下文
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//ApplicationListener 用于接收启动流程中广播的事件
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
(1) 收集对应的primarySources
primarySources个人理解为启动配置配置类,是后面上下文配置类递归解析的入口。primarySources类上并不一定需要@SpringApplication注解。@SpringApplication只是一个复合注解方便快速配置,只要不嫌麻烦我们甚至不使用这个注解,或者在多个类上使用改注解。
(2) 决定应用类型
根据classPath中是否包含一些特定的Class来判断web应用类型,后面在创建上下文时需要根据应用类型来创建,web应用类型分以下三种:
-
NONE非web应用
-
SERVLET基于
servletweb应用,例如依赖了Spring MVC -
REACTIVEREACTIVE式Web应用,例如Spring Reactive
(3) SPI机制ApplicationContextInitializer
通过SpringFactoriesLoader加载 "META-INF/spring.factories"下 key=org.springframework.context.ApplicationContextInitializer的类,也就是SPI机制加载上下文初始化器。用于在ApplicationContext刷新前(prepareContext)执行,对ApplicationContext进行初始化操作。
(4) SPI机制加载ApplicationListener
和步骤(3)一样的加载方式,只不过此处是加载ApplicationListener应用监听器。用于处理EventPublishingRunListener广播出来的事件。
ps:为什么Spring 提供了事件机制,Spring Boot 为什么要重新弄一份事件机制?
答案:在Spring Boot启动流程中Spring 容器的启动只占据一部分,所以需要新的事件机制。
(5) 找到MainClass
main方法所在的类
2、运行SpringApplication
在run方法中可以看到运行的主体流程
public ConfigurableApplicationContext run(String... args) {
//计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//1、加载并启动 运行时监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//广播正在启动事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2、创建环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置需要忽略的Bean
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
//3、创建上下文
context = createApplicationContext();
//异常报告
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//4、准备上下文 主要将环境以及参数还有启动配置类注册到上线文中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新上下文
refreshContext(context);
//钩子函数允许子类进行扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//广播启动完成事件
listeners.started(context);
//6、调用runner 即实现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;
}
(1)获取并启动运行监听器-SpringApplicationRunListener
用于Spring Boot 启动流程中各个阶段事件的广播以提供扩展机制-作用于不同阶段。因为ApplicationContext refresh之前,其事件机制还未初始化(refresh中初始化了事件机制)所以无法用于Spring boot Application启动流程中使用,所以才需要补充(对应Spring 事件机制的补充)。 创建了一个SpringApplicationRunListeners,里面包含了SpringFactoriesLoader(SPI)机制读取的"META-INF/spring.factories"下的 key=org.springframework.boot.SpringApplicationRunListener所指定的监听器。默认情况下只有EventPublishingRunListener。Spring Boot 在启动过程中依次会广播(EventPublishingRunListener)以下事件。
-
start()
开始启动时
ApplicationStartingEvent -
environmentPrepared(ConfigurableEnvironment environment)
环境准备完成 prepareEnvironment
ApplicationEnvironmentPreparedEvent -
contextPrepared(ConfigurableApplicationContext context)
**
ApplicationContextInitializer**执行完毕后ApplicationContextInitializedEvent -
contextLoaded(ConfigurableApplicationContext context)
在注册Source后也就是Spring Boot启动配置类后
ApplicationPreparedEvent -
started(ConfigurableApplicationContext context)
容器刷新完成
ApplicationStartedEvent -
running(ConfigurableApplicationContext context)
刷新完成并执行runner后
ApplicationReadyEvent -
failed(ConfigurableApplicationContext context, Throwable exception)
捕获到Trowable
ApplicationFailedEvent
//获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听器
listeners.starting();
//---------------------------------------
//创建了一个SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
//SPi机制读取SpringApplicationRunListener全类名指定的监听器
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener-广播给ApplicationListener
在广播事件时,遍历在创建SpringApplication时SPI所加载的**ApplicationListener进行事件广播。其原理就是通过ApplicationEventMulticaster**事件广播器(此时是SimpleApplicationEventMulticaster)广播事件。
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);
}
}
ApplicationEventMulticaster用于管理ApplicationListener并广播事件(ApplicationEvent)
(2)创建运行时环境
创建环境(PropertySources以及Profiles)并对环境进行配置,并绑定到SpringApplication。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment 创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//广播环境已经准备好事件
listeners.environmentPrepared(environment);
//绑定到 SpringApplication
bindToSpringApplication(environment);
//不是web环境的化就进行转化
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//将ConfigurationPropertySource附加到environment上
ConfigurationPropertySources.attach(environment);
return environment;
}
(3)创建ApplicationContext
按照指定applicationContextClass去创建上下文(也就是Spring 容器),如果没有指定则根据**webApplicationType也就是容器类型创建不同ApplicationContext**,对应关系如下:
-
SERVLET
AnnotationConfigServletWebServerApplicationContext -
REACTIVE
AnnotationConfigReactiveWebServerApplicationContext -
其他
AnnotationConfigApplicationContext
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
//得到指定的contextClass
Class<?> contextClass = this.applicationContextClass;
//没有指定的话则按类型创建
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
(4)准备ApplicationContext!!!
容器 refresh前的准备工作,对上下文进行配置。特别注意**ApplicationContextInitializer-在刷新之前的回调接口**,完成一些初始化操作针。最后将启动类(primarySources和setSources)注册到context中,也就是说将Spring 将以这些启动配置类为入口逐渐解析并加载所配置BeanDefinition。这一步最为重要可以看到Spring Boot 是怎么和Spring 联系起来的-将environment、启动配置类(sources)、参数等等注册到ConfigurableApplicationContext 中。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//-------------刷新前的准备一些初始化操作-------------
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境
context.setEnvironment(environment);
//后置处理上下文 允许子类做扩展
postProcessApplicationContext(context);
//执行上下文初始化器ApplicationContextInitializer 也就是创建SpringApplication时SPI机制读取那些初始化器
applyInitializers(context);
//广播上下文已经准备好了事件
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans 注册Spring boot参数、springBootBanner
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册参数
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
//设置BeanDefinition是否可以重写
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//配置懒加载的BeanFactory后置处理器
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//最为重要的一 将sources加载进Context 也就是将启动类 注册到上下文!!!!
load(context, sources.toArray(new Object[0]));
//广播context已经load完毕时间
listeners.contextLoaded(context);
}
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
(5)刷新上下文-启动容器
开始初始化Spring容器,在此之间注册了ShutdownHook也就是在JVM运行时注册一个钩子函数-当JVM关机时关闭容器。
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
//注册ShutdownHook
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
//刷新Spring容器 也就是启动Spring容器
refresh((ApplicationContext) context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
//初始化(启动)Spring容器入口
applicationContext.refresh();
}
(6)容器刷新后置处理
用于容器刷新后调用,如果需要特殊的刷新后处理,则重写该方法,用于子类的扩展。
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
//子类扩展
}
(7)执行Runners-启动后扩展点
Spring Boot 提供了一种机制用于在Spring Boot程序启动后执行一次性的特定程序。分为以下两种
ApplicationRunnerCommandLineRunner
此时在Spring启动后,所以从容器中取出ApplicationRunner和CommandLineRunner,排序后遍历执行。也是Spring Boot 提供的扩展点
callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//获取所有的runners
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//根据Order排序
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
总结
@SpringBootApplication只是一个复合注解,可以快速配置Spring Boot启动所需要的配置 最终注册到Spring中,什么自动装配、组件扫描等等实际生效还是在Spring 中。
Spring Boot基于约定大于配置理念以及自动装配,让我们可以快速开发一个Spring 应用,而Spring的目的是简化开发,Spring Boot可以认为是Spring 的脚手架。