SpringBoot自动配置原理流程

前言

新公司太忙了,都没啥空更新博客,就随便记录一下以前的学习笔记吧。SpringBoot是基于Spring上的衍生框架,只要看懂了Spring的话,学这个就比较简单了;SpringBoot也是在当前微服务时代下流行的框架,并且该框架采用了自动配置,所以只要简单的配置一下就可以直接使用了,省去了很多做配置的时间,可以说是开箱即用。当前SpringBoot版本号为2.1.15.RELEASE版本

使用SpringBoot

这个可以借鉴一下官网:快速创建SpringBoot Initializer

我们新建一个maven项目,然后在pom.xml添加一下两种依赖中的一种即可

<!--添加父模块-->
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.15.RELEASE</version>
</parent>
<!--添加依赖管理-->
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.15.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

由于在SpringBoot框架中采用模块化形式,所以如果想要使用哪些模块,可直接引入spring-boot-starter-xxxx模块,例如:

<!-- test模块 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Web模块 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 操作数据库模块 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 操作redis模块 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

引用好对应的模块之后,接下来就是打包了,一般采用SpringBoot自己封装的打包plugin工具,打成可执行包,如果要打成普通的引入jar包的话,那就不需要引入这个plugin了;

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins>
</build>

然后就是启动了,在主类加上相应的配置,就可以使用了

@SpringBootApplication
public class App {public static void main( String[] args ) {SpringApplication.run(App.class, args);}
}

启动原理

简单的使用讲过,接下来开始讲讲源码啦

@SpringBootApplication

注解主要作用是标识当前类为启动类,通过SpringBoot打包后的jar中找到该类,并启动当前应用,常用的属性有:

exclude:去除指定自动配置类。

scanBasePackages:扫描路径同ComponentScan一致

该注解源码中包含一下注解:

//设置为配置类相当于@Configuration,可自行点进去阅读
@SpringBootConfiguration
//自动配置注解,该注解包含@Import注解
@EnableAutoConfiguration
//开启扫描路径,默认为启动类同路径下所有包
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@EnableAutoConfiguration

该注解为表示自动配置,SpringBoot中自动配置,开箱即用的精髓都在于这个自动配置注解,该注解引用了@Import注解,去扫描spring.factories文件下的

org.springframework.boot.autoconfigure.EnableAutoConfiguration

 对应的的自动配置类,并加载相关Bean注入到IOC容器当中

SpringApplication

该类是应用启动的主类,点进去看一下会发现其实就是创建SpringApplication对象并且调用run方法:

创建对象

读取累路径下META-INF/spring.factories文件中的相关属性,并且拿到对应的Class对象

@SuppressWarnings({ "unchecked", "rawtypes" })
public 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();//读取spring.factories中ApplicationContextInitializer相关配置setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//读取spring.factories中ApplicationListener相关配置setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}

在SpringBoot默认的spring.factories文件中对应的ApplicationListener和ApplicationContextInitializer对应的示例:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

拿到这些类,并执行对应的方法:

ApplicationContextInitializer

配置在META-INF/spring.factories文件中,配置示例如上,继承接口并实现initialize方法,在Spring初始化之前执行该方法:

public class AppListenerStarter implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("AppListenerStarter ... ");}}

SpringApplicationRunListener

配置在META-INF/spring.factories文件中,配置示例如上,继承接口并实现initialize方法,在Spring初始化之前执行该方法:

public class AppListener implements SpringApplicationRunListener {//必须要有参构造方法public AppListener(SpringApplication application, String[] args) {}@Overridepublic void starting() {System.out.println("AppListener starting...");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {Object o = environment.getSystemProperties().get("os.name");System.out.println("AppListener environmentPrepared..." + o);}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("AppListener contextPrepared...");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("AppListener contextLoaded...");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("AppListener ConfigurableApplicationContext started...");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("AppListener running...");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("AppListener failed...");}}

执行结果为:

 调用run方法

//计时器,用来记录Spring容器启动花费的时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//从spring.factories文件中获取SpringApplicationRunListener实现类
SpringApplicationRunListeners listeners = getRunListeners(args);
//开始执行对应的监听器
listeners.starting();
try {//封装命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//封装监听器和参数ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);//打印Banner日志,就是那个SpringBoot的大LOGOBanner printedBanner = printBanner(environment);//创建指定SpringIOC容器context = createApplicationContext();//获取spring.factories文件中SpringBootExceptionReporter对应的Class,默认值为FailureAnalyzersexceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//执行监听器中的contextPrepared()方法prepareContext(context, environment, listeners, applicationArguments, printedBanner);//刷新IOC容器,即调用Spring中的refresh方法(扫描,加载),优先加载业务组件中的,然后在加载spring.factories文件中自动配置的类refreshContext(context);afterRefresh(context, applicationArguments);//计时停止stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//开始调用监听器中的started方法listeners.started(context);//回调ApplicationRunner和CommandLineRunner接口的实现类方法callRunners(context, applicationArguments);
}
catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);
}try {//调用监听器running方法listeners.running(context);
}
catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);
}
return context;

至此,SpringBoot启动流程结束了,自动配置就是将spring.factories文件中的指定配置类全部加载到容器中,优先加载业务Bean,后加载自动配置类中的Bean。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537094.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法:对象方式数组去重

var arr [3, 1, 1, 4 , 2 , 4 , 2 , 4 , 2, 1, 1, 3, 3, 3];var ary[];var obj{};for(var i0;i<arr.length;i){var curarr[i];if(!obj[cur]){obj[cur]cur;ary.push(cur);}}console.log(ary); 复制代码

python实现路由功能_python 实现重启路由器

有一些服务&#xff0c;需要动态IP&#xff0c;所以我们用重启路由器的方法实现。人工重启不可选&#xff0c;用定时脚本执行即可。贴代码&#xff0c;每种路由器&#xff0c;提示不一样。需要路由器有telnet功能才行。#!/usr/bin/env python# -*- coding: utf-8 -*-import tel…

SpringBoot自定义Starter(自动配置类)

前言 SpringBoot其实从诞生以来围绕的核心就是快速构建项目&#xff0c;快速构建的前提是有人帮你做好轮子&#xff0c;开发者只要拿来即用就好了&#xff0c;而造好轮子的人就是SpringBoot的开发者&#xff0c;引入自动配置的形式帮助开发者快速创建项目&#xff0c;而自动配…

Java并发编程之synchronized关键字解析

前言 公司加班太狠了&#xff0c;都没啥时间充电&#xff0c;这周终于结束了。这次整理了Java并发编程里面的synchronized关键字&#xff0c;又称为隐式锁&#xff0c;与JUC包中的Lock显示锁相对应&#xff1b;这个关键字从Java诞生开始就有&#xff0c;称之为重量级锁&#xf…

raidrive安装失败_记一次RaiDrive映射OneDrive遇到的问题

大概在1周以前&#xff0c;出于需要存放直播录像的原因&#xff0c;根据别人的视频教程去自己动手搞了个5T网盘的帐号。(体验一下&#xff0c;其实我还同时存一份在百度云&#xff0c;怕不稳定)用RaiDrive创建OneDrive的映射&#xff0c;在这步骤点确定后&#xff0c;会弹出微软…

通过代理模式 + 责任链模式实现对目标执行方法拦截和增强功能

前言 最近需要实现一个插件功能&#xff0c;但是如果做成两个接口的话&#xff08;即执行前和执行后&#xff09;&#xff0c;那么会降低插件的可玩性&#xff0c;所以需做成类似AOP的环绕通知形式&#xff0c;所以就使用到了责任链模式和代理模式进行实现。 介绍 代理模式(P…

Javascript基础之-原型(prototype)

首先呢&#xff0c;prototype是对象里的一个内置属性&#xff0c;并且呢&#xff0c;这个属性是对于其他对象的一个引用。所以呢&#xff0c;思考下面的例子&#xff1a; var obj {a: 2 } var myObj Object.create(obj); console.log(myObj.a); // 2 console.log(myObj obj)…

Oracle查询今天、昨天、本周、上周、本月、上月数据

查询今天数据&#xff1a; SELECT COUNT(1) FROM T_CALL_RECORDS WHERE TO_CHAR(T_RKSJ,YYYY-MM-DD)TO_CHAR(SYSDATE,YYYY-MM-DD)&#xff1b; 查询昨天数据&#xff1a; SELECT COUNT(1) FROM T_CALL_RECORDS WHERE TO_CHAR(T_RKSJ,YYYY-MM-DD)TO_CHAR(SYSDATE-1,YYYY-MM-DD)&…

usb一转多 树莓派zero_树莓派 Zero USB/以太网方式连接配置教程

树莓派 Zero 之所以成为一款非常棒的单板计算机并不全因为它小巧的尺寸和便宜的价格&#xff0c;还得益于它便捷、易用的特性。在加装了 Zero Quick Plug 或 microUSB/USB 转换头之后&#xff0c;将树莓派 Zero 和电脑连接起来。树莓派 Zero 即可配置成 USB/以太网设备&#xf…

vscode Go 1.11.4 编译错误 need Delve built by Go 1.11 or later

更新golang的版本为1.11.4之后vscode编译错误&#xff1a;executables built by Go 1.11 or later need Delve built by Go 1.11 or later 原因是delve的版本太老了&#xff0c;需要更新&#xff0c;且delve的github地址已经更换&#xff0c;很多教程里的地址是不对的 新地址安…

oppo的sd卡在哪里打开_oppo的sd卡在哪里打开

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。以oppo A91为例&#xff0c;其sd卡可直接在文件管理页面的存储里面即可打开。OPPO A91的屏幕为6.4英寸&#xff0c;主屏分辨率2400乘以1080像素&#xff0c;机身颜色有暗夜星辰&…

Navicat使用教程:使用Navicat Query Analyzer优化查询性能(第1部分)

下载Navicat Monitor最新版本Navicat Monitor 是一套安全、简单而且无代理的远程服务器监控工具。它具有强大的功能使你的监控发挥最大效用。受监控的服务器包括 MySQL、MariaDB 和 Percona Server&#xff0c;并与 Amazon RDS、Amazon Aurora、Oracle Cloud、Microsoft Azure …

dg oracle 切换模式_Oracle数据库 DGbroker三种保护模式的切换

1.三种保护模式– Maximum protection在Maximum protection下&#xff0c; 可以保证从库和主库数据完全一样&#xff0c;做到zero data loss.事务同时在主从两边提交完成&#xff0c;才算事务完成。如果从库宕机或者网络出现问题&#xff0c;主从库不能通讯&#xff0c;主库也立…

软件包管理

应用程序&#xff1a;程序&#xff1a;Architecture C语言&#xff1a;源代码-->&#xff08;编译&#xff09; 二进制格式脚本&#xff1a;解释器&#xff08;二进制程序&#xff09; 源代码-->编译-->链接-->运行程序&#xff1a;指令数据指令&#xff1a;芯片CP…

工业机器人码垛教学实施_工业机器人应用案例码垛详解

工业机器人应用案例码垛详解随着科技的进步以及现代化进程的加快&#xff0c;人们对搬运速度的要求越来越高&#xff0c;传统的人工码垛只能应用在物料轻便、尺寸和形状变化大、吞吐量小的场合&#xff0c;这已经远远不能满足工业的需求&#xff0c;机器人码垛机应运而生。机器…

第一家云创大数据产业学院在佛山职业技术学院挂牌

2019年1月10日&#xff0c;“云创大数据产业学院揭牌暨战略合作协议签署仪式”在佛山职业技术学院电子信息学院会议室举行。云创大数据总裁刘鹏教授、市场部经理单明月&#xff0c;佛山职业技术学院电子信息学院院长唐建生、副院长田钧、学院办公室主任赵雪章、信息工程系主任乔…

String与StringBuffer和StringBuilder的根本区别

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

16进制 ksh_AIX系统中如何统计进程打开的文件数目

作者&#xff1a;李烨楠 中国建设银行来自微信公众号&#xff1a;平台人生环境: AIX 6.1 AIX7.1前言:用户有时需要统计一个进程打开的文件数目&#xff0c;比如&#xff0c;在当前打开文件句柄使用量是否超过用户资源限制(/etc/security/limits)中 nofiles的取值时。那么&#…

前端Http协议缓存初解

[TOC] 简介 用户获取网络资源&#xff0c;需要通过非常长的网络去服务器上请求资源,另外服务端为了应对大量的用户请求而不断的提升硬件性能与带宽。这对用户与服务端都非常的不友好。而缓存就是为了解决用户请求速度与释放服务器压力而生的。 为什么我会写Http缓存&#xff0c…

详解java访问修饰符

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…