SpringBoot原理讲解
一.问题的引入
首先我们来看一个最简单的例子。
我们先创建一个SpringBoot的工程,如何创建一个SpringBoot工程就不说了,不会请自行解决。然后写一个controller类,通过请求路径,返回HelloWorld在浏览器页面上显示。


上面两张图就是程序的一个整体的结构和运行的结果,那么问题来了,SpringBoot程序中没有任何配置,不像Spring框架,写一大堆配置信息在xml文件中,那么程序是怎么将我们这里的Controller类扫描到spring容器中的呢?
二.原理讲解。
首先第一点,我们来看一下SpringBoot的启动类,
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//启动SpringBoot应用
SpringApplication.run(DemoApplication.class, args);
}
}
其中,我们看到一个注解@SpringBootApplication,SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
然后我们点进去看看这个注解到底包含了什么,
@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、@EnableAutoConfiguration
下面我们逐个来讲解:
@SpringBootConfiguration这个注解顾名思义,标注在某个类上,这个类就是SpringBoot的配置类,我们再点进去看看。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration
原来这个注解是底层是Spring中的注解@Configuration,而@Configuration注解底层就是一个@Component,代表一个容器。
SpringBoot的精髓是在@EnableAutoConfiguration这个注解上,根据名字,我们可以知道这是一个自动配置类,也就是能够实现一些自动配置的功能,那么具体是配置了什么东西呢?
我们再点进去看看
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)
我们看见了和这个注解有关联的重要的两个注解@AutoConfigurationPackage,@Import(AutoConfigurationImportSelector.class),也是分别给大家讲解一下。
1.@AutoConfigurationPackage
@AutoConfigurationPackage:这是一个自动配置的包,我们来看下在这个程序中是配置了哪个包,点进来
@Import(AutoConfigurationPackages.Registrar.class)
再点到Register,我们看到在大概123行的位置,有一段代码。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
我们看到这个方法器中一个参数metadata,代表注解的元信息,然后我们通过这个注解的元信息来获这个包名,我们在这打个断点,debug运行,可以看见一些元信息的基本内容,最主要我们要知道是导入了哪个包,我们选中new PackageImport(metadata).getPackageName(),右键计算一下

发现导入的包就是我们项目的根目录,假如我们新建一个包叫com.controller,在里面写一个controller类,大家可以运行的试一下是否能成功呢。
2.@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class):这个注解代表的是一个自动配置的选择器,那么要导入哪些组件的选择器呢?
我们点进去,看到非常重要的一段。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
这里我们获得到了一个configurations 的list集合,我们打个断点看看这个集合中到底给我们装了些什么东西。

我们发现这个configurations 里面装配了124个自动配置类,原来我们没有配置的一些信息,都通过SpringBoot的自动配置类给我配置好了。
那么我还想知道这些类SpringBoot程序给我们放到哪了呢,我们看到getCandidateConfigurations(annotationMetadata, attributes);
点进去,我们看到:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass()
那么再点进去我们看看到底加载的工厂名称是啥,
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
我们看到其中类加载器给我们获取了资源,我们点进去,
点了这么多次,终于找到我们想要的答案了!!!
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
原来这些自动配置类都在类路径下的"META-INF/spring.factories"

还有下面的autoconfigure包中的内容,我们也看看

这里面我们所有所用到的配置类全部由SpringBoot给我们配置了,所以我们知道SpringBoot表面上是零配置的,其实底层都给我们封装好了,也是方便我们程序员进行开发。
那么有些人肯定又有疑惑,SpringBoot怎么知道给我们自动配置哪些类呢?
欲知后事如何,请关注一下作者,纯手打码字不易,也是希望和大家多多交流,一起学习,谢谢!

SpringBoot原理讲解的更多相关文章
- 【SpringBoot】单元测试进阶实战、自定义异常处理、t部署war项目到tomcat9和启动原理讲解
========================4.Springboot2.0单元测试进阶实战和自定义异常处理 ============================== 1.@SpringBoot ...
- OAuth的机制原理讲解及开发流程
本想前段时间就把自己通过QQ OAuth1.0.OAuth2.0协议进行验证而实现QQ登录的心得及Demo实例分享给大家,可一直很忙,今天抽点时间说下OAuth1.0协议原理,及讲解下QQ对于Oaut ...
- pureMVC简单示例及其原理讲解五(Facade)
本节将讲述Facade,Proxy.Mediator.Command的统一管家.自定义Facade必须继承Facade,在本示例中自定义Facade名称为ApplicationFacade,这个名称也 ...
- pureMVC简单示例及其原理讲解四(Controller层)
本节将讲述pureMVC示例中的Controller层. Controller层有以下文件组成: AddUserCommand.as DeleteUserCommand.as ModelPrepCom ...
- pureMVC简单示例及其原理讲解三(View层)
本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...
- php 变量原理讲解
php 变量原理讲解 一.变量概念 所谓变量,是指在程序中其值可以变化的量. 程序是管理和处理数据的.在程序运行过程中,我们需要存贮这些数据,变量和常量就是用于保存程序运行时的数据的. 变量通常由 ...
- ElasticSearch之 控制相关度原理讲解
控制相关度 相关度评分背后的理论 如何计算评分的 Lucene 使用布尔模型(Boolean model) 查找匹配文档 并主要的借鉴了 词频/逆向文档频率(term frequency/invers ...
- 深入springboot原理——动手封装一个starter
从上一篇文章<深入springboot原理——一步步分析springboot启动机制(starter机制)> 我们已经知道springboot的起步依赖与自动配置的机制.spring-bo ...
- MongoDB优化,建立索引实例及索引机制原理讲解
MongoDB优化,建立索引实例及索引机制原理讲解 为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样 ...
随机推荐
- 精心整理(含图版)|你要的全拿走!(R数据分析,可视化,生信实战)
本文首发于“生信补给站”公众号,https://mp.weixin.qq.com/s/ZEjaxDifNATeV8fO4krOIQ更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号. 为 ...
- BigInt 的使用!
今天学长讲的卡特兰数真的是卡的一批,整个全是高精的题,这时我就使用重载运算符,然后一下午就过去了 首先来看一波水题(也就卡了2小时) . A. 网格 内存限制:512 MiB 时间限制:1000 ms ...
- 从Gartner IT Symposium,看RPA“一半是海水一半是火焰”
2019年,艺赛旗和Gartner建立了咨询合作,并在企业发展策略中汲取了Gartner的部分建议.今年Gartner在全球召开了多场IT Symposium,并在Symposium发布2020的相关 ...
- [LINQ2Dapper]最完整Dapper To Linq框架(五)---查看Linq实际执行的SQL
此例子是使用LINQ2Dapper封装,效率优于EntityFramwork,并且支持.NetFramework和.NetCore框架,只依赖于Dapper支持.net framework4.6.1及 ...
- PHP判断数组下标有没有存在的方法
PHP判断数组下标有没有存在的方法<pre>if(!empty($token['errcode'])){ print_r($token['errmsg']); exit();}</p ...
- java多线程与线程并发二:线程互斥
本文章内容整理自:张孝祥_Java多线程与并发库高级应用视频教程 当两条线程访问同一个资源时,可能会出现安全隐患.以打印字符串为例,先看下面的代码: // public class Test2 { p ...
- SparkSQL--数据源Parquet的加载和保存
一.通用的load和save操作 对于Spark SQL的DataFrame来说,无论是从什么数据源创建出来的DataFrame,都有一些共同的load和save操作.load操作主要用于加载数据,创 ...
- 【Java】面向对象之封装
面向对象编程是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改.封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问.要访问该类的数据,必须通过指定的方式 ...
- Linux注意事项
一.学习 Linux 的注意事项 1. Linux 严格区分大小写 Linux 是严格区分大小写的,这一点和 Windows 不一样,所以操作时要注意区分大小写的不同,包括文件名和目录名.命令.命令选 ...
- 领扣(LeetCode)二叉树的中序遍历 个人题解
给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 递归的思路很简单,不再累 ...