SpringBoot -- SpringBoot的启动整体过程 | 自动配置类解析注册过程 | Spring5 源码解析
Posted 做猪呢,最重要的是开森啦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot -- SpringBoot的启动整体过程 | 自动配置类解析注册过程 | Spring5 源码解析相关的知识,希望对你有一定的参考价值。
- 1. SpringBoot启动类注解@SpringBootApplication:
- 2. SpringBoot的启动:
- 2.1. getSpringFactoriesInstances:
- 2.2. SpringApplication#run:
- 2.3. AbstractApplicationContext#refresh:
- 2.4. invokeBeanFactoryPostProcessors:
- 2.5. parser.parse(candidates);
- 2.6. this.reader.loadBeanDefinitions(configClasses)
- 2.7. ServletWebServerApplicationContext#onRefresh
- 2.8. AbstractApplicationContext#finishBeanFactoryInitialization
- 3. 小结:
基础环境: spring-boot :2.3.3.RELEASE、jdk1.8
1. SpringBoot启动类注解@SpringBootApplication:
标注
@SpringBootApplication
的类就是SpringBoot的主配置类,启动该类来启动应用,这是一个组合注解
@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 {
主要关注自动配置类注解
@EnableAutoConfiguration
,这也是个组合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
这里主要关注
@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
`
对于@AutoConfigurationPackage,也是一个组合注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
至此,可以看见整个@SpringBootApplication会引入两个@Import,要留意下
AutoConfigurationImportSelector.class
2. SpringBoot的启动:
启动主配置类即可启动SpringBoot项目,启动过程是怎样的,下面来看看,先进入启动类的run方法,会调用到此处:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
在构造函数中会进行一些参数赋值,值得注意的是,此时开始预解析META-INF/spring.factories文件的配置类名
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// web应用类型,通常默认SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置用于容器初始化的ApplicationContextInitializer
// 初次调用时,getSpringFactoriesInstances会进行META-INF/spring.factories文件的配置类名的解析
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 同理是设置用于容器初始化的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1. getSpringFactoriesInstances:
该方法会调用SpringFactoriesLoader.loadFactoryNames方法进行预解析配置类名
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 会获取默认类加载器,Launcher类型
ClassLoader classLoader = getClassLoader();
// 获取类型为type的配置类名,本次为获取ApplicationContextInitializer类型的配置类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过默认构造方法创建对应配置类名的实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对实例进行调用排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.1.1. SpringFactoriesLoader.loadFactoryNames:
该方法会调用loadSpringFactories来解析META-INF/spring.factories文件的配置类名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 参数传递过来的类的完全限定名,即ApplicationContextInitializer类的完全限定名
String factoryTypeName = factoryType.getName();
// loadSpringFactories方法来解析配置类名;getOrDefault获取key为factoryTypeName的配置类名
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 从缓存cache(key:类加载器;value: result)中获取默认类加载器对应的value
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
// 初次启动调用,缓存获取不到值,开始解析META-INF/spring.factories文件的配置类名
try {
// 类加载器不为null,取所有类路径下的META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 循环遍历有META-INF/spring.factories文件的jar包,将里面的键值对封装到result,结构相当于 Map<Stirng,LinkedList>
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);
}
}
像自动配置类名就是从spring-boot-autoconfigure这个jar包META-INF/spring.factories文件解析出来
2.2. SpringApplication#run:
public ConfigurableApplicationContext run(String... args) {
... ... ...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 上面设置一些环境吧应该,没怎么关注
configureIgnoreBeanInfo(environment);
// 这里控制台会打印输出那个Spring启动图
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext即ioc容器
context = createApplicationContext();
// 像上面2.1那样,会从缓存中获取SpringBootExceptionReporter类型实例
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 对context进行一些预处理,主要是一些赋值
// 这里会将主配置类封装成BeanDefinition再注册到ApplicationContext中,详情可参考文末参考链接1
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新context容器,重点方法
refreshContext(context);
... ...
}
2.3. AbstractApplicationContext#refresh:
上述refreshContext方法会刷新上下文,即context容器,方法最后会调用refresh方法:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 刷新前准备,初始化properties等
prepareRefresh();
// 获取ApplicationContext中组合的BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 主要给beanFactory赋值,比如类加载器,ApplicationContextAwareProcessor后置处理器等等
prepareBeanFactory(beanFactory);
try {
// 允许在上下文的子类中对bean factory进行后处理
postProcessBeanFactory(beanFactory);
// 调用Bean工厂的后置处理器,重点方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化消息源
initMessageSource();
// 初始化事件广播
initApplicationEventMulticaster();
// 初始化特殊的Bean,比如tomcat
onRefresh();
// 注册监听器
registerListeners();
// 实例化所有的(non-lazy-init)单例Bean
finishBeanFactoryInitialization(beanFactory);
// 发布刷新完毕事件
finishRefresh();
}
... ... ...
}
2.4. invokeBeanFactoryPostProcessors:
通过委托模式调用PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
... ... ...
}
2.4.1. invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
- 这个方法会实例化和调用所有postProcessBeanDefinitionRegistry
- 还有其父类BeanFactoryPostProcessor的postProcessBeanFactory方法
- 注意:BeanDefinitionRegistryPostProcessor优先于BeanFactoryPostProcessor执行,BeanFactoryPostProcessor优先于bean实例化执行;
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// ioc容器创建时beanFactory为DefaultListableBeanFactory
// DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,所以true
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 循环已经注册过的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 如果是子类BeanDefinitionRegistryPostProcessor,就执行postProcessBeanDefinitionRegistry方法
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// 用于保存本初要执行的BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 找出所有实现BeanDefinitionRegistryPostProcessor接口的Bean的beanName
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// 判断这个ppName的bean是否实现了PriorityOrdered
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 满足条件会通过getBean获取对应ppName的实例,添加到currentRegistryProcessors
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 将ppName添加到processedBeans,表名这个ppName的bean调用过postProcessBeanDefinitionRegistry方法
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 调用postProcessBeanDefinitionRegistry方法,重点方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
... ... ...
// 下面省略了两次查找postProcessorNames及调用postProcessBeanDefinitionRegistry方法的逻辑,与上面类似
// 也省略对父类BeanFactoryPostProcessor查找和调用的逻辑,也类似
// 省略的内容可以见文末参考链接2
}
- postProcessorNames查找其实是从beanDefinitionNames中查找符合类型的beanName
- 容器context创建后,默认的BeanFactory有五个内部托管的beanName,在prepareContext会将主配置类的beanName添加进去
- 这里实现BeanDefinitionRegistryPostProcessor的只有internalConfigurationAnnotationProcessor
这个beanName在getBean时会获取ConfigurationClassPostProcessor对象
2.4.2. invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
本次会调用ConfigurationClassPostProcessor的currentRegistryProcessors方法
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
// 循环遍历传过来的postProcessors,调用postProcessBeanDefinitionRegistry方法
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
会通过registryId去重,重复则抛异常,反则调用processConfigBeanDefinitions
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
2.4.3. processConfigBeanDefinitions(registry);
方法入参registry就是BeanFactory,就是默认的DefaultListableBeanFactory
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 循环遍历candidateNames,也就是beanDefinitionNames,检查出是配置类的beanName
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.de以上是关于SpringBoot -- SpringBoot的启动整体过程 | 自动配置类解析注册过程 | Spring5 源码解析的主要内容,如果未能解决你的问题,请参考以下文章