配置一个springboot启动类,然后运行即可
@SpringBootApplication
public class SpringBootDemo {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemo.class, args);
}
}
@SpringBootApplication
注解
@SpringBootConfiguration
注解
@SpringBootConfiguration
: 这个注解是 Spring Boot 中的一个特殊标记,它本质上是一个 @Configuration
注解的替代品。@Configuration
类是 Java-based 配置的核心,它提供了一种声明式的方式来注册 beans,替代了传统的 XML 配置。
@SpringBootConfiguration
注解用于指示一个类是 Spring Boot 的配置类,它包含了 Spring 应用程序的定义和配置。
@SpringBootConfiguration
本身被标注为 @Configuration
,这意味着被 @SpringBootConfiguration
标注的类可以包含 Spring 的 @Bean
方法,用于定义 Spring 容器中的 beans。
@ComponentScan
注解
这个注解用于启动组件扫描。组件扫描是 Spring 自动检测并注册 beans 的过程。
@ComponentScan
注解会扫描指定的包路径,查找带有 @Component
、@Service
、@Repository
、@Controller
等注解的类,并将它们注册为 Spring 容器中的 beans。
如果没有指定具体的包路径,默认会扫描标注 @ComponentScan
注解的类所在的包及其子包。
@EnableAutoConfiguration
注解
这个注解是 Spring Boot 自动配置特性的关键。它告诉 Spring Boot 根据添加到项目中的 jar 依赖自动配置 Spring 应用。Spring Boot 会根据 classpath 下的设置、beans 和各种属性配置来猜测并配置你想要的配置。
例如,如果 classpath 下有 Spring MVC 和 Thymeleaf,@EnableAutoConfiguration
会自动配置你的应用程序为一个 Spring MVC 应用,并使用 Thymeleaf 作为模板引擎。这个注解使得 Spring Boot 应用能够快速启动和运行,减少了大量的配置工作。
@EnableAutoConfiguration
注解主要是通过 @AutoConfigurationPackage
和 @Import
注解实现自动配置的功能。
@AutoConfigurationPackage
注解
这个注解的作用是将使用 @EnableAutoConfiguration
的类的包注册为自动配置的包。这样,Spring Boot 会扫描这个包及其子包下的所有组件,并将它们注册到 Spring 上下文中。
@Import(AutoConfigurationImportSelector.class)
注解
@Import
注解用于导入配置类或选择器。在这里,它导入了 AutoConfigurationImportSelector
类,这个类会查找并加载所有可用的自动配置类。
AutoConfigurationImportSelector
使用 SpringFactoriesLoader.loadFactoryNames
方法来加载 classpath 下 META-INF/spring.factories
文件中列出的所有自动配置类。这些自动配置类通常是由 Spring Boot 和其他第三方库提供的。
AutoConfigurationImportSelector
的一部分功能是用于查找 META-INF/spring
目录下与传入的注解类型相对应的配置文件。例如,如果传入的注解是 @EnableAutoConfiguration
,那么路径将是 META-INF/spring/org.springframework.boot.autoconfigure.EnableAutoConfiguration.imports
。
SpringApplication.run()
创建SpringApplication
对象
启动类的
main
方法:javapublic static void main(String[] args) { SpringApplication.run(SpringBootDemo.class, args); }
这是Spring Boot应用程序的入口点。它调用
SpringApplication.run()
方法,并将主类SpringBootDemo.class
作为参数传递,这个类标记有@SpringBootApplication
。重载的
run
方法:javapublic static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); }
这个方法是一个便利方法,它将单个主源类转换为一个数组,然后调用接受类数组的
run
方法。创建
SpringApplication
实例并运行:javapublic static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
这里,
SpringApplication
的实例被创建,并且立即调用其run
方法来启动应用程序。primarySources
是包含主启动类的数组,args
是传递给应用程序的命令行参数。SpringApplication
构造函数:javapublic SpringApplication(Class<?>... primarySources) { this(null, primarySources); }
这个构造函数实际上是一个转发构造函数,它调用了另一个带有
ResourceLoader
参数的构造函数,这里默认为null
。完整的
SpringApplication
构造函数:查看代码
javapublic 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.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
在这个构造函数中,执行了以下操作:
- 设置资源加载器。
- 确认主源不为空,并将其添加到一个
LinkedHashSet
集合中。 - 推断Web应用程序类型,这决定了Spring Boot将创建哪种类型的Web服务器。
- 获取并设置引导注册初始化器。
- 获取并设置应用程序上下文初始化器。
- 获取并设置应用程序事件监听器。
- 推断主应用程序类,这通常用于日志记录和其他目的。
总结来说,当调用SpringApplication.run()
方法时,Spring Boot会创建一个SpringApplication
实例,该实例会进行一系列初始化操作,包括设置资源加载器、推断应用程序类型、设置初始化器和监听器等。最后,它会启动Spring应用程序上下文,并加载和配置应用程序的Bean。
推断应用程序类型
查看代码
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这个方法检查类路径上是否存在特定的类来推断应用程序的类型。如果存在响应式的WebFlux指示类且不存在Servlet相关的类,则推断为响应式Web应用程序。如果不存在Servlet指示类,则推断为非Web应用程序。否则,推断为标准的Servlet Web应用程序。
获取初始化器
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
getSpringFactoriesInstances
实际调用到forDefaultResourceLocation
方法,读取META-INF/spring.factores,将SpringApplicationRunListeners类型存到集合中。
获取初监听器
其实监听器和初始化的操作是基本一样的,不同的是传入的是ApplicationListener.class
参数,而初始化传入的是ApplicationContextInitializer.class
。
定位main方法
deduceMainApplicationClass()
方法通过 findMainClass()
方法遍历调用栈,寻找包含 main
方法的类,这个类就是Spring Boot应用程序的入口点。这个过程对于日志记录和诊断信息很有用,因为它帮助确定了应用程序的启动类。
run
方法
源码
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}
run
方法是 Spring Boot 应用程序启动的入口点。这个方法负责启动 Spring Boot 应用程序,包括环境的准备、应用程序上下文的创建和刷新、以及异常处理等。
创建Startup实例
Startup startup = Startup.create();
创建一个Startup
实例,用于跟踪和记录应用程序启动过程中的时间和状态。
注册关闭钩子
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
如果设置了注册关闭钩子,那么会添加一个关闭钩子到JVM,以确保在JVM关闭时能够优雅地关闭Spring应用程序。
创建BootstrapContext
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
创建一个BootstrapContext
,它是一个用于初始化ApplicationContext
之前的上下文,可以用来加载早期配置和初始化器。
配置无头模式属性
configureHeadlessProperty();
配置系统属性java.awt.headless
,通常用于确保在服务器环境中运行时不会尝试进行图形界面操作。
获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
获取所有的SpringApplicationRunListeners
,并通知它们应用程序正在启动。
准备应用参数和环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
创建应用参数实例,并准备环境(包括属性源和 profiles),同时通知监听器环境准备完成。
打印Banner
Banner printedBanner = printBanner(environment);
打印Spring Boot的Banner,如果有的话。
创建ApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
创建ApplicationContext
实例,并设置启动时的性能指标收集器。
准备ApplicationContext
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
准备ApplicationContext
,包括设置环境、添加后处理器、初始化器,以及应用事件监听器。
prepareContext
源码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
addAotGeneratedInitializerIfNecessary(this.initializers);
applyInitializers(context);
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 AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
if (this.keepAlive) {
context.addApplicationListener(new KeepAlive());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
if (!AotDetector.useGeneratedArtifacts()) {
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
}
listeners.contextLoaded(context);
}
prepareContext
方法是SpringApplication启动过程中的一个关键步骤,它负责准备ApplicationContext
,使其能够接收Bean定义并启动。以下是prepareContext
方法中的详细步骤:
- 设置环境:java将准备好的环境对象
context.setEnvironment(environment);
ConfigurableEnvironment
设置到ApplicationContext
中,这样应用程序就可以访问属性源和profiles。 - 后处理ApplicationContext:java提供一个钩子方法,允许子类在
postProcessApplicationContext(context);
ApplicationContext
被设置到SpringApplication之前对其进行自定义处理。 - 添加AOT生成的初始化器:java如果存在AOT(Ahead-Of-Time)编译生成的初始化器,则将它们添加到初始化器列表中。
addAotGeneratedInitializerIfNecessary(this.initializers);
- 应用初始化器:java运行所有注册的
applyInitializers(context);
ApplicationContextInitializer
实例,这些初始化器可以对ApplicationContext
进行自定义处理,例如设置环境属性或添加Bean定义。 - 通知监听器上下文已准备:java通知所有注册的
listeners.contextPrepared(context);
SpringApplicationRunListeners
,ApplicationContext
已经准备好了,但还未加载Bean定义。 - 关闭BootstrapContext:java关闭
bootstrapContext.close(context);
BootstrapContext
,因为ApplicationContext
已经准备好了。 - 记录启动信息:java如果启用了启动信息日志记录,则记录应用程序的启动信息和激活的profiles。
if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); }
- 注册特定的单例Bean:java将
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); }
ApplicationArguments
和Banner
作为单例Bean注册到BeanFactory
中,这样它们就可以在应用程序中被注入和使用。 - 设置BeanFactory属性:java设置
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } }
BeanFactory
的属性,例如是否允许循环引用和Bean定义覆盖。 - 添加懒加载后处理器:java如果启用了懒加载,则添加一个
if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }
BeanFactoryPostProcessor
,用于延迟初始化Bean。 - 添加保持活跃的监听器:java如果启用了保持活跃,则添加一个
if (this.keepAlive) { context.addApplicationListener(new KeepAlive()); }
ApplicationListener
,以确保ApplicationContext
在JVM关闭时不会立即关闭。 - 添加属性源排序后处理器:java添加一个
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
BeanFactoryPostProcessor
,用于确保属性源按照特定的顺序进行排序。 - 加载源:java如果不是使用AOT生成的工件,则加载所有的源(通常是主启动类和其他配置类),并将它们注册到
if (!AotDetector.useGeneratedArtifacts()) { // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); }
ApplicationContext
中。 - 通知监听器上下文已加载:java通知所有注册的
listeners.contextLoaded(context);
SpringApplicationRunListeners
,ApplicationContext
已经加载了Bean定义,但还未刷新。
prepareContext
方法完成了ApplicationContext
的准备工作,使其能够接收并处理Bean定义,为接下来的刷新和启动步骤做好准备。
刷新ApplicationContext
refreshContext(context);
刷新ApplicationContext
,执行Bean定义的加载、BeanFactory的初始化、调用BeanFactoryPostProcessors和BeanPostProcessors等。
refreshContext
调用Servlet类型的refresh()
源码
// AbstractApplicationContext类中
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (RuntimeException | Error ex ) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
contextRefresh.end();
}
}
finally {
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
refreshContext
方法是Spring Boot启动过程中的一个关键步骤,它负责刷新ApplicationContext
,即初始化和配置所有的Bean。以下是refresh
方法的详细步骤,它通常在refreshContext
中被调用:
锁定启动和关闭锁:
javathis.startupShutdownLock.lock();
锁定启动和关闭的锁,以确保在刷新过程中不会进行其他的启动或关闭操作。
记录当前线程:
javathis.startupShutdownThread = Thread.currentThread();
记录当前线程,用于后续的检查和操作。
启动刷新步骤的跟踪:
javaStartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
如果启用了性能跟踪,则开始跟踪刷新步骤。
准备刷新:
javaprepareRefresh();
准备刷新上下文,包括设置其状态标志和初始化属性源。
获取新的BeanFactory:
javaConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
获取一个新的BeanFactory实例,这可能包括销毁之前的BeanFactory并创建一个新的。
准备BeanFactory:
javaprepareBeanFactory(beanFactory);
设置BeanFactory的属性,如类加载器、后处理器等,并注册一些特殊的Bean,如环境Bean。
后处理BeanFactory:
javapostProcessBeanFactory(beanFactory);
提供一个钩子方法,允许子类在BeanFactory准备好之后对其进行自定义处理。
启动BeanFactory后处理器的跟踪:
javaStartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
如果启用了性能跟踪,则开始跟踪BeanFactory后处理器的步骤。
调用BeanFactory后处理器:
javainvokeBeanFactoryPostProcessors(beanFactory);
调用所有注册的BeanFactoryPostProcessor,这些处理器可以修改Bean的定义。
注册Bean后处理器:
javaregisterBeanPostProcessors(beanFactory);
注册所有的BeanPostProcessor,这些处理器可以拦截Bean的创建过程。
结束BeanFactory后处理器的跟踪:
javabeanPostProcess.end();
如果启用了性能跟踪,则结束BeanFactory后处理器的步骤。
初始化消息源:
javainitMessageSource();
初始化用于国际化消息支持的消息源。
初始化事件多播器:
javainitApplicationEventMulticaster();
初始化用于发布事件的
ApplicationEventMulticaster
。刷新特定的上下文子类:
javaonRefresh();
提供一个钩子方法,允许子类在刷新过程中执行特定的操作。
注册监听器:
javaregisterListeners();
检查并注册所有实现了
ApplicationListener
接口的Bean。完成BeanFactory的初始化:
javafinishBeanFactoryInitialization(beanFactory);
实例化所有非懒加载的单例Bean。
完成刷新:
javafinishRefresh();
发布
ContextRefreshedEvent
事件,通知监听器上下文已经刷新。处理异常:
javacatch (RuntimeException | Error ex) { // ... }
如果在刷新过程中发生运行时异常或错误,则记录警告,销毁已创建的Bean,重置活动标志,并抛出异常。
结束刷新步骤的跟踪:
javacontextRefresh.end();
如果启用了性能跟踪,则结束刷新步骤的跟踪。
释放锁:
javathis.startupShutdownLock.unlock();
释放启动和关闭的锁,允许其他启动或关闭操作。
refresh
方法完成了Spring应用程序上下文的初始化,包括Bean的定义、加载和实例化,以及事件的发布等。这是Spring Boot启动过程中的一个核心步骤,它确保了应用程序的配置和Bean的正确加载。
调用BeanFactory后处理器
Spring容器启动时调用BeanFactory后处理器的一部分执行流程:
- 调用
invokeBeanFactoryPostProcessors
方法,传入BeanFactory和手动注册的BeanFactory后处理器。 - 首先处理实现了
BeanDefinitionRegistryPostProcessor
接口的后处理器。 - 遍历手动注册的BeanFactory后处理器,如果是
BeanDefinitionRegistryPostProcessor
类型,则直接调用其postProcessBeanDefinitionRegistry
方法。 - 从BeanFactory中获取所有实现了
BeanDefinitionRegistryPostProcessor
接口的Bean的名称。 - 按照实现了
PriorityOrdered
和Ordered
接口的顺序,分别调用这些Bean的postProcessBeanDefinitionRegistry
方法。 - 调用
invokeBeanDefinitionRegistryPostProcessors
方法,循环调用所有BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry
方法。 postProcessBeanDefinitionRegistry
方法内部会调用processConfigBeanDefinitions
方法,解析配置类。- 在
processConfigBeanDefinitions
中,找出所有配置类Bean定义,进行排序后,创建ConfigurationClassParser
解析器。 - 调用
ConfigurationClassParser
的parse
方法递归解析每个配置类。 - 在
parse
方法中,首先处理成员类,然后处理@PropertySource
注解,扫描@ComponentScan
注解,处理@Import
注解,处理@ImportResource
注解,最后处理@Bean
方法。 - 解析
@ComponentScan
注解时,会扫描指定包路径下的Bean,并递归解析为配置类。 - 解析
@Import
注解时,会导入指定的类,并递归解析为配置类。 - 解析完成后,所有配置类会被注册到
BeanDefinitionRegistry
。 - 最后,调用所有
BeanFactoryPostProcessor
的postProcessBeanFactory
方法。
整个执行流程就是Spring解析处理所有配置类,并将Bean定义注册到BeanFactory的过程。其中涉及到对各种注解的处理,以及后处理器的调用。
一部分源码
invokeBeanFactoryPostProcessors(beanFactory);
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&
beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// WARNING: Although it may appear that the body of this method can be easily
// refactored to avoid the use of multiple loops and multiple lists, the use
// of multiple lists and multiple passes over the names of processors is
// intentional. We must ensure that we honor the contracts for PriorityOrdered
// and Ordered processors. Specifically, we must NOT cause processors to be
// instantiated (via getBean() invocations) or registered in the ApplicationContext
// in the wrong order.
//
// Before submitting a pull request (PR) to change this method, please review the
// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
// to ensure that your proposal does not result in a breaking change:
// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry registry) {
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor registryProcessor) {
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
@Override
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);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry sbr) {
singletonRegistry = sbr;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.propertySourceRegistry = (this.environment instanceof ConfigurableEnvironment ce ?
new PropertySourceRegistry(new PropertySourceProcessor(ce, this.resourceLoader)) : null);
this.registry = registry;
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
parser.parse(candidates);
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = null;
try {
sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"I/O failure while processing configuration class [" + sourceClass + "]", ex);
}
this.configurationClasses.put(configClass, configClass);
}
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(), org.springframework.context.annotation.PropertySource.class,
PropertySources.class, true)) {
if (this.propertySourceRegistry != null) {
this.propertySourceRegistry.processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Search for locally declared @ComponentScan annotations first.
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class,
MergedAnnotation::isDirectlyPresent);
// Fall back to searching for @ComponentScan meta-annotations (which indirectly
// includes locally declared composed annotations).
if (componentScans.isEmpty()) {
componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(),
ComponentScan.class, ComponentScans.class, MergedAnnotation::isMetaPresent);
}
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;
}
这个方法是ConfigurationClassParser
类中的一个核心方法,它的职责是处理一个给定的配置类(ConfigurationClass
),解析其中的各种注解,并将相关的Bean定义注册到Spring容器中。
方法签名:
@Nullable
注解表示该方法可能返回null
。ConfigurationClass configClass
:当前正在处理的配置类。SourceClass sourceClass
:当前配置类的源类信息,包含了类的元数据。Predicate<String> filter
:一个过滤器,用于确定是否应该跳过某些类的处理。
方法执行流程:
- 处理成员类: 如果配置类注解了
@Component
,递归处理任何成员(嵌套)类。javaprocessMemberClasses(configClass, sourceClass, filter);
- 处理
@PropertySource
注解: 遍历配置类上的所有@PropertySource
注解,并将它们添加到环境配置中。javafor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(...)) { // ... }
- 处理
@ComponentScan
注解: 查找配置类上直接声明或通过元注解声明的@ComponentScan
注解。如果存在,执行扫描操作,并将扫描到的Bean定义注册到容器中。如果扫描到的Bean定义也是配置类,递归解析它们。javaSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(...); // ... for (AnnotationAttributes componentScan : componentScans) { // ... }
- 处理
@Import
注解: 解析配置类上的@Import
注解,导入指定的类,这些类可以是普通的Java类、配置类或者实现了ImportSelector
或ImportBeanDefinitionRegistrar
接口的类。导入的类也会被递归解析。javaprocessImports(configClass, sourceClass, getImports(sourceClass), filter, true);
- 处理
@ImportResource
注解: 解析配置类上的@ImportResource
注解,加载指定的资源文件(通常是XML配置文件)。javaAnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(...); // ...
- 处理
@Bean
方法: 查找配置类中所有标注了@Bean
的方法,并将它们添加到配置类的beanMethods
集合中。这些方法将在后续的Bean定义注册阶段被调用。javaSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); // ...
- 处理接口的默认方法: 如果配置类实现的接口中包含默认方法(Java 8+特性),则处理这些默认方法。java
processInterfaces(configClass, sourceClass);
- 处理父类: 如果配置类有父类,并且父类不是Java原生类型,递归处理父类。java
if (sourceClass.getMetadata().hasSuperClass()) { // ... return sourceClass.getSuperClass(); }
- 完成处理: 如果没有父类需要处理,方法返回
null
,表示配置类的处理完成。javareturn null;
这个方法体现了Spring框架的组件扫描、注解驱动和Java配置的核心原理,是Spring容器启动过程中非常关键的一步。通过这个方法,Spring能够将用户定义的配置类转换成容器内部的Bean定义,并最终实例化Bean。
刷新后操作
afterRefresh(context, applicationArguments);
提供一个钩子方法,允许子类在刷新ApplicationContext
之后执行自定义操作。
记录启动信息
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
记录应用程序启动完成的信息,并通知监听器应用程序已经启动。
调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
调用所有实现ApplicationRunner
和CommandLineRunner
接口的Bean。
处理启动过程中的异常
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
如果启动过程中发生异常,将捕获异常并处理,例如关闭ApplicationContext
并通知监听器启动失败。
准备就绪通知
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
如果ApplicationContext
正在运行,则通知监听器应用程序已经准备好。
处理准备就绪过程中的异常
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
如果在准备就绪过程中发生异常,将捕获异常并处理。 最后,run
方法返回创建并启动的ConfigurableApplicationContext
实例,它是Spring Boot应用程序的核心。