SpringBoot启动流程与自动装配
是什么
Spring Boot基于Spring框架之上的一个微服务架构开发框架
大大简化了Spring的开发。因为Spring Boot提供了大量的自动配置。而且它是基于Java配置方式的开发(全注解)
Spring Boot与集成了许多第三方框架并进行了最小化自动配置
Spring Boot和Spring Cloud关系:Spring Cloud的开发需要用Spring Boot,反之不一定
官方介绍
Spring Boot使创建独立的、生产级的基于Spring的应用程序变得很容易,您可以“直接运行”这些应用程序。
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
我们对Spring平台和第三方库有自己的看法,这样您就可以轻松入门了。大多数Spring启动应用程序只需要很少的Spring配置。
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.
特点
创建独立的Spring应用程序
Create stand-alone Spring applications
直接嵌入Tomcat、Jetty或Undertow(不需要部署WAR文件)
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
提供独到的起始依赖去简化配置
Provide opinionated 'starter' dependencies to simplify your build configuration
在可能的情况下自动配置Spring和第三方库
Automatically configure Spring and 3rd party libraries whenever possible
提供可用于生产的特性,如度量标准、健康状况检查和外部化配置
Provide production-ready features such as metrics, health checks and externalized configuration
完全不需要代码生成,也不需要XML配置 去XML化
Absolutely no code generation and no requirement for XML configuration
启动流程
- 创建 SpringApplication
- 保存一些信息。
- 判定当前应用的类型。ClassUtils。Servlet
- bootstrappers:初始启动引导器(List):去spring.factories文件中找 org.springframework.boot.Bootstrapper
- 找 ApplicationContextInitializer 应用初始化器,去spring.factories找 ApplicationContextInitializer
- List<ApplicationContextInitializer<?>> initializers
- 找 ApplicationListener ,应用监听器。去spring.factories找 ApplicationListener
- List<ApplicationListener<?>> listeners
- 运行 SpringApplication
- StopWatch
- 记录应用的启动时间
- 创建引导上下文(Context环境)createBootstrapContext()
- 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
- 让当前应用进入headless模式。java.awt.headless
- 获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
- getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
- 遍历 SpringApplicationRunListener 调用 starting 方法;
- 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
- 保存命令行参数;ApplicationArguments
- 准备环境 prepareEnvironment();
- 返回或者创建基础环境信息对象。StandardServletEnvironment
- 配置环境信息对象。
- 读取所有的配置源的配置属性值。
- 绑定环境信息
- 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
- 创建IOC容器(createApplicationContext())
- 根据项目类型(Servlet)创建容器,
- 当前会创建 AnnotationConfigServletWebServerApplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext()
- 保存环境信息
- IOC容器的后置处理流程。
- 应用初始化器;applyInitializers;
- 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
- 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
- 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
- 刷新IOC容器。refreshContext
- 创建容器中的所有组件(Spring注解)
- 容器刷新完成后工作?afterRefresh
- 所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
- 调用所有runners;callRunners()
- 获取容器中的 ApplicationRunner
- 获取容器中的 CommandLineRunner
- 合并所有runner并且按照@Order进行排序
- 遍历所有的runner。调用 run 方法
- 如果以上有异常,
- 调用Listener 的 failed
- 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
- running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
启动类
1 @SpringBootApplication
2 @EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
3 @EnableScheduling
4 public class Application {
5
6 public static void main(String[] args) {
7 SpringApplication.run(Application.class, args);
8 }
SpringApplication.run方法
new sprngApplication()创建
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
} 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还是react
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
run运行
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//启停监听器
StopWatch stopWatch = new StopWatch();
//记录启动时间
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//当前应用进入headless自力更生模式
configureHeadlessProperty();
//获取所有运行时监听器 通过SpringFactoryInstance去spring.factories下找SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//保存传过来的args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境信息 包括加载外部配置源 调用listener.envirementPrepared()通知监听器环境准备完
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置需要忽略的信息
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//创建IOC容器 根据当前项目类型 servlet or reactive 这里是servlet
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备IOC容器信息
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器 创建容器中的所有组件
refreshContext(context);
//刷新后的处理 暂时没干任何工作
afterRefresh(context, applicationArguments);
//监听启动完成花费多少时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//通知监听器项目已经启动
listeners.started(context);
//调用所有能遍历到的runner,获取ApplicationRunner和CommandLineRuner 执行它们的run方法
callRunners(context, applicationArguments);
}
//如果有异常,调用listeners.failed 容器创建失败
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
//调用listener的running方法,通知监听器程序已经进入runnig状态
listeners.running(context);
}
catch (Throwable ex) {
//如果有异常,调用listeners.failed 容器创建失败
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回整个IOC容器
return context;
}
准备IOC容器信息
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//保存基础环境
context.setEnvironment(environment);
//IOC容器的后置处理
postProcessApplicationContext(context);
//应用之前的那些初始化器,对IOC容器记性初始化扩展
applyInitializers(context);
//通知监听器上下文准备完成
listeners.contextPrepared(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 DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 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);
}
刷新IOC容器 创建容器中的所有组件
private void refreshContext(ConfigurableApplicationContext context) {
//spring核心源码 刷新底层的ApplicationContext。
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
//向JVM运行时注册一个关闭钩子,在JVM关闭时关闭这个上下文
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
} /**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
/**
*spring经典的整个初始化过程
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 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); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // 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 (BeansException 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 {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
自动装配原理
引入
@springbootApplication注解中自动配置相关
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{} ======================
@EnableAutoConfiguration中有@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage进行组件注册,@Import(AutoConfigurationImportSelector.class)导入的AutoConfigurationImportSelector组件调用loadSpringFactories()方法,利用工厂加载,扫描当前系统里面所有META-INF/spring.factories位置的文件,得到所有自动配置组件,
但是会按需配置,这个是通过@Condition注解族实现的,比如@ConditionalOnMissingBean指定以用户自己配置的优先
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {} //利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfigurati
按需开启自动配置
可以修改默认配置
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
总结
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
SpringBoot启动流程与自动装配的更多相关文章
- SpringBoot启动代码和自动装配源码分析
随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot源码学习3——SpringBoot启动流程
系列文章目录和关于我 一丶前言 在 <SpringBoot源码学习1--SpringBoot自动装配源码解析+Spring如何处理配置类的>中我们学习了SpringBoot自动装配如何实现 ...
- SpringBoot启动流程及其原理
Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...
随机推荐
- Redis 源码解读之 AOF Rewrite
- sqlserver update join
update a set a.UserAgent = b.UserAgent from InfoVisitDetails a inner join InfoVisitDetails b on a.IP ...
- (四)Mysql之索引介绍
索引数据结构的选择:Hash表.二叉树.平衡二叉树.(红黑树近似于平衡二叉树).B树.B+树1)Hash表:Java的HashMap.TreeMap就是Hash表结构,以键值对存储,时间复杂度是O(1 ...
- 四,redis6版本的使用部署
继第三章(https://www.cnblogs.com/123456likun/p/13841540.html) 官网发布最新的6版本,有新的数据类型出现,给了小编我极大的动力,决定写几张关于最新的 ...
- 概率生成函数(PGF)简记
基本搬运自<浅谈生成函数在掷骰子问题上的应用>. 对于定义在非负整数上的离散随机变量 \(X\),级数 \(F(z) = \sum\limits_{i\ge 0} \operatornam ...
- 面了几个说自己精通 Vue 的同学,实在一言难尽……
请说一下响应式数据的原理 默认 Vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属性,当页面到对应属性时,会进行依赖收集 (收集当前组件中 ...
- 基于AD9361的双收双发射频FMC子卡
FMC177-基于AD9361的双收双发射频FMC子卡 一.板卡介绍 FMC177射频模块分别包含两个接收通道与发射通道,其频率可覆盖达到70MHz~6GHz,AD9361芯片提供具有成本效益的实验平 ...
- python 非阻塞线程对话框,非qt(解决qt MessageBox使用线程时候卡死问题)
def msg_okbox(self, strinfo, isYesno=False): if isYesno: return win32api.MessageBox(None, strinfo, & ...
- Java面向对象之什么是继承?
继承 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模. extends的意思是"扩展".子类是父类的扩展. Java中类只有单继承,没有多继承!即 一个儿子只能有一个爸 ...
- Transformers Pipelines
pipelines 是使用模型进行推理的一种很好且简单的方法.这些pipelines 是从库中抽象出大部分复杂代码的对象,提供了一个简单的API,专门用于多个任务,包括命名实体识别.屏蔽语言建模.情感 ...