1、SpringBoot自动装配的原理:
总结:
SpringBoot启动时会通过@EnableAutoConfiguration注解下的@Import注解导入的AutoConfigurationImportSelector类的selectImports()方法通过SpringFatoriesLoader.loadFactoryNames()扫描META-INF/spring.factories配置文件中的所有自动配置类, 这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表并对其进行加载,这些自动配置类都是以AutoConfiguration结尾命名的,实际上是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类取得全局配置文件中的属性,而XXXProperties类是通过@ConfigurationProperties注解与全局配置文件的属性进行绑定的
SpringBoot启动类上有一个@SpringBootApplication注解,这是一个复合注解,由
@SpringBootConfiguration 代表当前是一个配置类
@EnableAutoConfiguration 开启自动装配
@ComponentScan 扫描组件包
组成,而@EnableAutoConfiguration也是一个复合注解,由
@AutoConfigurationPackage 自动配置包,将MainApplication所在包下所有组件导入
@Import({AutoConfigurationImportSelector.class}) 自动装配的核心
AutoConfigurationImportSelector类下的selectImport()方法调用SpringFactoriesLoader.loadFactoryNames()方法扫描具有META-INF/spring.factories的jar包
以spring-boot-autoconfigure-xxx.jar包为例,它的spring.factories文件是一组组的key-value形式,里面存放的是这个jar包引用的具体依赖的自动配置类,这些
自动配置类又通过@EnableConfigurationProperties注解支持通过xxxProperties.class读取application.properties或application.yml文件中的属性值,如果没有
配置值,就是用默认值,这也就是所谓的约定大于配置,例如其中一个key是xxxEnableAutoConfiguration,value值是一个xxxAutoConfiguration的类名列表,例如
RabbitAutoConfiguration、RedisAutoConfiguration等,点进去其实是一个配置类,例如上述对应的是RabbitAutoConfiguration.class和RedisAutoConfiguration.class
在springboot启动时,SpringApplication.run()方法内部执行selectImports()方法,根据自动配置类的全限定名加载对应的class,将所有自动配置类加载到spring容器中
举例:spring-boot:2.2.4.RELEASE这个jar包
spring-boot:2.2.4.RELEASE--->META-INF/spring.factories--->(k/v)EnableAutoConfiguration/RabbitAutoConfiguration、RedisAutoConfiguration...
RabbitAutoConfiguration点进去是RabbitAutoConfiguration.class---->@EnableConfigurationProperties({RabbitProperties.class})
RabbitProperties.class--->@ConfigurationProperties(prefix = "spring.rabbitmq")对应application.properties/application.yml配置文件的属性值
2、SpringBoot的启动流程:
总结:
SpringBoot启动流程分为两大步:创建SpringApplication对象、执行SpringApplication对象的run方法
创建SpringApplication对象:
配置source
检查是否为web环境
创建初始化构造器(自动装配操作)
创建应用监听器
配置应用主方法(main方法)的所在类
执行SpringApplication对象的run方法:
创建并启动计时器,在启动结束后打印启动时间
创建一个空的容器对象
获取并启动监听器,后续的每个阶段都会发送对应事件到监听器
配置环境ConfigurableEnvironment,这是springboot应用要使用的环境
打印banner
给前面创建的空对象context进一步的创建,根据不同的web类型,通过反射获取对应的容器对象
给创建的容器进行
前置处理(设置上下文环境、执行所有ApplicationContextInitializer对象的初始化方法、发送上下文准备完成事件、加载bean到上下文、发送上下文加载完成事件)
刷新(真正进行ApplicationContext的初始化和创建bean)
后置处理
停止计时,发出启动结束事件,如果有异常进行处理抛出,没有异常返回创建的容器context
源码:
SpringBoot启动流程大概分为两步:return (new SpringApplication(primarySources)).run(args);分别是创建SpringApplication对象和执行run方法
一、创建SpringApplication对象:基本上是做一些必要的属性初始化和赋值操作
①配置primarySource
②根据classPath检查是否是web应用
③创建初始化容器(构造器)Initializer
④创建监听器
⑤配置应用主方法所在类(main方法所在类)
创建SpringApplication对象方法:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//把SpringApplication作为primarySource属性存储
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//从classPath推断是否为web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化容器(Initializer),最终会调用这些功能
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器(Listener)
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//获取main方法所在的类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
二、执行run方法(运行spring应用程序)
①启动一个计时器,在整个容器启动完成(启动类启动完成)会打印耗时
②创建一个空的容器对象context
③获取并启动监听器SpringApplicationRunListeners,在对应的阶段发送对应的事件到监听器
当调用run方法后立即调用,用于最早期的初始化
环境准备好之后调用
在启动失败之后调用...
④配置环境ConfigurableEnvironment,这是springboot应用要使用的环境(主要有创建环境、加载属性文件、配置监听)
//根据不同的web类型创建不同实现的Environment对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
//监听器发送环境配置完成事件
listeners.environmentPrepared((ConfigurableEnvironment)environment);
//配置文件中spring.main属性绑定到SpringApplication对象中
this.bindToSpringApplication((ConfigurableEnvironment)environment);
//如果用户使用spring.main.web-application-type属性手动设置了webApplicationType
if (!this.isCustomEnvironment) {
//将环境对象转换成用户设置的webApplicationType相关类型,它们继承的是同一父类
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
⑤banner的配置和打印,控制台输出的banner图形
⑥给前面创建的空对象context进一步的创建,根据不同的web类型,通过反射获取对应的容器对象
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
⑦给创建的容器进行前置处理、刷新、后置处理
//前置处理:设置上下文环境、执行所有ApplicationContextInitializer对象的初始化方法、发送上下文准备完成事件、加载bean到上下文、发送上下文加载完成事件
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置上下文环境
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
//执行所有ApplicationContextInitializer对象的initialize方法,这些对象是通过spring.factories文件加载,也就是自动装配那里的东西
this.applyInitializers(context);
//发布上下文准备完成事件到所有的监听器
listeners.contextPrepared(context);
.......省略一堆代码
//加载bean到上下文
this.load(context, sources.toArray(new Object[0]));
//发送上下文加载完成事件
listeners.contextLoaded(context);
}
//刷新容器:这里真正进行ApplicationContext的初始化和创建bean,下面是AbstractApplicationContext抽象类的刷新方法,具体的实现参考这里
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
//第一步:准备刷新容器的预备工作:设置容器的启动时间、设置活跃状态为true、设置关闭状态为false、获取Environment对象,加载属性值、准备监听器
this.prepareRefresh();
//第二步:创建BeanFactory对象:DefaultListableBeanFactory,加载xml文件属性值到当前工厂
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//第三步:对BeanFactory做预备工作:对各种属性进行填充
this.prepareBeanFactory(beanFactory);
try {
//第四步:允许在上下文子类中对BeanFactory进行PostProcessing:子类覆盖方法做额外处理,此处一般不做任何扩展工作
this.postProcessBeanFactory(beanFactory);
//第五步:调用各种BeanFactory处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//第六步:注册bean处理器,这里只是注册功能,真正调用是getBean方法
this.registerBeanPostProcessors(beanFactory);
//第七步:初始化MessageSource
this.initMessageSource();
//第八步:初始化事件监听多路广播器
this.initApplicationEventMulticaster();
//第九步:留给子类来初始化其他的bean
this.onRefresh();
//第十步:在注册的bean中查找listener bean,加载到消息广播器
this.registerListeners();
//第十一步:初始化剩下的单实例
this.finishBeanFactoryInitialization(beanFactory);
//第十二步:完成容器刷新,发布相应事件
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
//为了防止bean资源异常占用,在异常处理时销毁前面生成的单例bean
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
//后置处理:一个空的方法
⑧停止计时,发出启动结束事件,如果有异常进行处理抛出,没有异常返回创建的容器context
run方法:
public ConfigurableApplicationContext run(String... args) {
//计时工具
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建启动上下文对象
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//第一步:获取并启动监听器,在对应的阶段发送对应的事件到监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
//第二步:准备环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//第三步:打印banner,启动时的打印的spring图案
Banner printedBanner = this.printBanner(environment);
//第四步:创建spring容器
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//第五步:spring容器的前置处理
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//第六步:刷新容器
this.refreshContext(context);
//第七步:spring容器后置处理
this.afterRefresh(context, applicationArguments);
//启动完成后结束计时,这就是我们控制台打印的启动时间
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//发出启动结束事件
listeners.started(context);
//执行runner的run方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
//异常处理,如果run过程发生异常,处理并抛出
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
//返回最终构建的容器对象
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
SpringBoot框架的自动装配和启动原理
最新推荐文章于 2025-10-27 11:47:09 发布
本文详细解析了SpringBoot的自动配置原理,包括@EnableAutoConfiguration注解如何引导加载自动配置类,以及spring.factories文件在其中的作用。同时,深入介绍了SpringBoot的启动流程,从创建SpringApplication对象到执行run方法的各个阶段,如环境配置、容器创建与刷新等关键步骤。
742

被折叠的 条评论
为什么被折叠?



