springboot内容总结

springboot源码学习过程记录

springboot 程序是通过 SpringApplication这个类来启动一个Java主程序
当前是基于springboot的2.6.13版本来学习的

调用SpringApplication的构造函数创建对象,然后调用run方法

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 当前应用程序的类型,返回SERVLET,有三种 SERVLET/ NONE/ REACTIVE
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这里做了很多事情

  1. 配置resourceLoader
  2. 存主类信息
  3. 判断当前应用程序的类型
  4. 加载Spring应用上下文初始化器,获取初始化器的实例对象(从spring.factory中拿到全限定类型,再通过反射做的)
  5. 加载监听器(ApplicationListener),设置监听器的实例对象(和上面一样,是加载了所有包下的spring.factory)
  6. 找到应用程序的主类,开始执行

然后执行run方法

执行run()方法,传args参数过去,之后是应用程序启动的整个过程

	public ConfigurableApplicationContext run(String... args) {
        // 程序启动时间
		long startTime = System.nanoTime();
        // 
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        // 上下文环境,创建context对象
		ConfigurableApplicationContext context = null;
        // 没什么用,设置系统参数中java.awt.headless值为true
		configureHeadlessProperty();
        // 创建监听器对象
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // 启动监听器
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
            // 设置参数args,就是命令行参数,传参数是以--开头,即--key=value
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

获取监听器

从spring.facotries中获取到类EventPublishingRunListener,通过反射实例化后返回。是一个事件发布运行监听器,在Spring Boot 启动过程中发布各种事件,通过一个多路广播器,将springboot运行状态的变化构建成事件,并广播给各个监听器
SpringApplicationRunListeners封装了所有的启动监听器

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
                // 从spring.factories文件中,通过SpringApplicationRunListener这个key,获取到对于的监听器
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
				this.applicationStartup);
	}

启动监听器

在运行过程中监听某些事件,不同的监听器监听的事件是不一样的
SpringApplicationRunListeners.java

	void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

EventPublishingRunListener.java

	@Override
	public void starting(ConfigurableBootstrapContext bootstrapContext) {
		this.initialMulticaster
				.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}

创建环境配置的类,加一些环境参数,准备运行的环境

SpringApplication.java

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
        // 环境准备,EventPublishingRunListener.environmentPrepared方法,从spring.factories中拿到
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = convertEnvironment(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

prepareContext方法

把启动类加载到上下文应用环境context里

refreshContext方法

实现自动化配置

springboot的自动装配过程

@SpringBootApplication 注解里有3个很重要的注解@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

@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注解:
表示这个类是一个配置类,它是@Configuration注解的派生注解,与@Configuration注解的功能一致。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

@AutoConfigurationPackage 注解:将主程序类所在包及所有子包下的组件到扫描到spring容器中。

AutoConfigurationImportSelector 是一个选择器,有一个方法getCandidateConfigurations,从spring.factory加载EnableAutoConfiguration对应的自动配置类

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	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;
	}
}

在refresh()方法里,使用invokeBeanFactoryPostProcessors(beanFactory)方法,通过反射来实例化所有需要注册的BeanFactoryPostProcessor的bean,在这个方法处理过程中,
最终使用processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);方法解析配置类
即使用ConfigurationClassParser 解析@Configuration注解标识的类
调用org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass方法,查看有没有加@ComponentScan注解,有注解则扫描包下的类,就可以扫描到加注解需要注入的类,解析成BeanDefinitionHolder,放到set集合里。

然后处理所有的@Import注解,加载import注解的属性值

之后this.deferredImportSelectorHandler.process()方法去处理,通过grouping.getImports()去调用AutoConfigurationImportSelector类里的方法,得到spring.factory里的EnableAutoConfiguration对应的配置类,加载进来,并过滤掉不需要的

class ConfigurationClassParser {
	@Nullable
	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
		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
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		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
		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;
	}
}

springboot启动过程:

每个springboot应用程序都有一个主入口,是一个main方法,在方法里调用SpringApplication.run()方法来启动程序,这个类必须加@SpringBootApplication注解

初始化

1. 判断当前应用程序的类型

有3种,默认返回SERVLET
NONE
SERVLET
REACTIVE

2. 加载所有的初始化容器

3. 加载所有的监听器

4. 找到程序运行的主类

开始执行run方法

1. 记录程序启动时间,创建bootstrapContext 上下文环境

		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();

2. 配置系统属性 java.awt.headless

3. 创建应用的监听器SpringApplicationRunListeners并开始监听。

		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);

4. 加载命令行的参数值

解析在命令行中(--key=value)输入的属性值,封装到AppplicationArguments对象中
new SimpleCommandLineArgsParser().parse(args)

5. 准备当前应用程序的环境

根据应用程序的类型SERVLET得到的
然后对象当前环境进行配置

		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());

调用listeners(EventPublishingRunListener)准备环境

		listeners.environmentPrepared(bootstrapContext, environment);

6. 配置系统属性,是否忽略一些bean的加载

配置项为(true/false):
spring.beaninfo.ignore

configureIgnoreBeanInfo(environment);

7. 生成banner对象,准备打印

a. 先判断是否有图片,支持jpg、gif、png格式的图片
b. 读取banner.txt文件中内容
c. 循环打印输入默认的banner

8. 准备上下文应用对象,根据应用程序类型(webApplicationType)判断创建哪种上下文对象

createApplicationContext();

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
	@Override
	public void setApplicationStartup(ApplicationStartup applicationStartup) {
		super.setApplicationStartup(applicationStartup);
		this.beanFactory.setApplicationStartup(applicationStartup);
	}
}

9. 设置ApplicationStartup到应用上下文context

			context.setApplicationStartup(this.applicationStartup);

10.准备上下文对象ConfigurableApplicationContext

根据当前应用程序的类型来判断创建哪种格式的上下文对象

			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

a. 初始化,想beanFactory中注入了三个postporcessor的对象,后续在字段装备的时候会用到
applyInitializers(context);
b. 执行监听器 listeners.contextPrepared(context);
c. 加载很多资源配置,完成自动装配
load(context, sources.toArray(new Object[0]));
d. listeners.contextLoaded(context);

11. 刷新上下文环境

12. refresh之后的扩展,默认是空的

			afterRefresh(context, applicationArguments);

13. 计时,打印启动程序耗时多长时间

14. 运行所有的监听器对象

			listeners.started(context, timeTakenToStartup);

BeanFactoryPostProcessor 和 BeanPostProcessor 的区别:

1. 作用阶段

  • BeanFactoryPostProcessor:
    是修改beanDefination的,在bean实例化之前执行的。即:容器加载完bean定义信息,封装成beanDefination,BeanFactoryPostProcessor 的 postProcessBeanFactory 方法会在所有的 BeanDefinition 加载完成之后,且在 bean 实例化之前被调用。

  • BeanPostProcessor:
    是修改bean的,在 Bean 实例化和 依赖注入完成后,以及 初始化方法调用前后执行。

2. 接口方法

  • BeanFactoryPostProcessor:
    定义了方法 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。
    该方法的参数是 BeanFactory,可以访问并修改 BeanDefinition。

  • BeanPostProcessor:
    定义了两个方法:
    postProcessBeforeInitialization(Object bean, String beanName):在 Bean 的初始化方法调用之前执行。
    postProcessAfterInitialization(Object bean, String beanName):在 Bean 的初始化方法调用之后执行。

posted @ 2024-12-10 23:21  二十四桥冷月夜  阅读(25)  评论(0)    收藏  举报