SpringBoot -- SpringBoot的启动整体过程 | 自动配置类解析注册过程 | Spring5 源码解析

Posted 做猪呢,最重要的是开森啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot -- SpringBoot的启动整体过程 | 自动配置类解析注册过程 | Spring5 源码解析相关的知识,希望对你有一定的参考价值。

基础环境:  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 源码解析的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot入门到精通-SpringBoot自定义starter

SpringBoot.06.SpringBoot日志管理

SpringBoot.06.SpringBoot日志管理

如何把springboot插件删除干净

Springboot 启动加载机制

SpringBoot入门到精通-SpringBoot集成SSM开发项目