一.问题的引入

首先我们来看一个最简单的例子。

我们先创建一个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原理讲解的更多相关文章

  1. 【SpringBoot】单元测试进阶实战、自定义异常处理、t部署war项目到tomcat9和启动原理讲解

    ========================4.Springboot2.0单元测试进阶实战和自定义异常处理 ============================== 1.@SpringBoot ...

  2. OAuth的机制原理讲解及开发流程

    本想前段时间就把自己通过QQ OAuth1.0.OAuth2.0协议进行验证而实现QQ登录的心得及Demo实例分享给大家,可一直很忙,今天抽点时间说下OAuth1.0协议原理,及讲解下QQ对于Oaut ...

  3. pureMVC简单示例及其原理讲解五(Facade)

    本节将讲述Facade,Proxy.Mediator.Command的统一管家.自定义Facade必须继承Facade,在本示例中自定义Facade名称为ApplicationFacade,这个名称也 ...

  4. pureMVC简单示例及其原理讲解四(Controller层)

    本节将讲述pureMVC示例中的Controller层. Controller层有以下文件组成: AddUserCommand.as DeleteUserCommand.as ModelPrepCom ...

  5. pureMVC简单示例及其原理讲解三(View层)

    本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...

  6. php 变量原理讲解

    php 变量原理讲解 一.变量概念   所谓变量,是指在程序中其值可以变化的量. 程序是管理和处理数据的.在程序运行过程中,我们需要存贮这些数据,变量和常量就是用于保存程序运行时的数据的. 变量通常由 ...

  7. ElasticSearch之 控制相关度原理讲解

    控制相关度 相关度评分背后的理论 如何计算评分的 Lucene 使用布尔模型(Boolean model) 查找匹配文档 并主要的借鉴了 词频/逆向文档频率(term frequency/invers ...

  8. 深入springboot原理——动手封装一个starter

    从上一篇文章<深入springboot原理——一步步分析springboot启动机制(starter机制)> 我们已经知道springboot的起步依赖与自动配置的机制.spring-bo ...

  9. MongoDB优化,建立索引实例及索引机制原理讲解

    MongoDB优化,建立索引实例及索引机制原理讲解 为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样 ...

随机推荐

  1. [考试反思]1001csp-s模拟测试(b):逃离

    如你所见,b组题,除了NC乱入直奔T2抢了我一个首杀以外A层学过FFT的人都没有参加. 竞争压力很小,题又简单,所以就造就了6个AK. 然而并不计入总分,我仍然稳在第二机房. T1lyl16分钟切掉我 ...

  2. P4409 [ZJOI2006]皇帝的烦恼(20190922B)(乱搞)

    考场历程十分艰辛啊... 第一题没切掉,还浪费了很长时间,就是一个裸的最小生成树,但是因为可恶的distance为关键字莫名其妙查错了10min.... 本题先乱搞了一下,过了样例 然后看第三题,可写 ...

  3. Redis 的底层数据结构(压缩列表)

    上一篇我们介绍了 redis 中的整数集合这种数据结构的实现,也谈到了,引入这种数据结构的一个很大的原因就是,在某些仅有少量整数元素的集合场景,通过整数集合既可以达到字典的效率,也能使用远少于字典的内 ...

  4. ORACLE存储过程的创建和执行的简单示例和一些注意点

    此示例的主要目的主要是为了了解在PL/SQL环境下怎么创建和执行存储过程. 存储过程所涉及的DataTable: 第一步:创建游标变量 游标是ORACLE系统在内存中开辟的一个工作区,主要用来存储SE ...

  5. linux下 驱动模块编译步骤

    本文将直接了当的带你进入linux的模块编译.当然在介绍的过程当中,我也会添加一些必要的注释,以便初学者能够看懂.之所以要写这篇文章,主要是因为从书本上学的话,可能要花更长的时间才能学会整个过程,因为 ...

  6. C语言I博客作业08

    这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 作业 我在这个课程的目标是 熟悉使用while和do-while结构解决问题 这个作业在那个具体方面帮助我实现目标 pta题目及查找的资料 ...

  7. Ansibile之playbook初识

    一.playbook简介 ansiblie的任务配置文件被称为playbook,俗称“剧本”,每一个剧本(playbook)中都包含了一系列的任务,这每个任务在ansible中又被称为“戏剧”(pla ...

  8. 字体图标转base64

    如果你在阿里矢量库下载了字体图标在项目引入无法显示时,可以把图标转成base64 在线转换的链接 https://transfonter.org/ css字体图标的制作

  9. 花了几个小时总结了一些容易出错的 Java 知识点!

    本文已经收录自 JavaGuide (61k+Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Star!欢迎骚扰!) 原文地址:https://javag ...

  10. mysql基础之约束

    约束的目的: 1.约束保证数据的完整性和一致性. 2.约束分为表级约束 和 列级 约束.(针对约束字段的数目的多少来确定的) 3.约束类型包括 not null (非空约束) primary key( ...