0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深度盘点一遍自动装配原理(下)

jf_78858299 来源:Java知音 作者: Object 2023-04-07 11:18 次阅读

主程序(重要)

//@SpringBootApplication 标注,是一个SpringBoot应用
@SpringBootApplication
public class SpringbootdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootdemoApplication.class, args);
    }
}

再写SpringBoot项目的时候,总要写这么一个主程序,这个主程序最大的特点就是其类上放了一个@SpringBootApplication注解,这也正是SpringBoot项目启动的核心,也是我们要研究的重点。

注意:之后的分析可能会深入源码,源码是一层一层嵌套的,所以光靠文字说明会比较难以理解,最好是自己在IDE环境下跟着一步一步跟着点下去。当然也可以绕过这一部分直接看结论。

点开@SpringBootApplication,可以发现它是一个组合注解,主要是由这么几个注解构成的。

@SpringBootConfiguration//核心
@EnableAutoConfiguration//核心
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

我们首先要研究的就是核心的两个注解 @SpringBootConfiguration@EnableAutoConfiguration ,逐个进行分析。

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

可以看到SpringBootConfiguration其实就携带了一个@Configuration注解,这个注解我们再熟悉不过了,他就代表自己是一个Spring的配置类。所以我们可以认为:@SpringBootConfiguration = @Configuration

@EnableAutoConfiguration

顾名思义,这个注解一定和自动配置相关,点进去看源代码之后可以发现,其内部就包含了这么两个注解。

@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class)//自动配置导入选择

来看看@Import(AutoConfigurationImportSelector.class)中的内容:

它帮我们导入了AutoConfigurationImportSelector,这个类中存在一个方法可以帮我们获取所有的配置,代码如下。

/*
  所有的配置都存放在configurations中,
  而这些配置都从getCandidateConfiguration中获取,
  这个方法是用来获取候选的配置。
*/
List

getCandidateConfigurations():

这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?

/*获取候选的配置*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

实际上它返回了一个List,这个List是由 loadFactoryNames() 方法返回的,其中传入了一个getSpringFactoriesLoaderFactoryClass(),我们可以看看这个方法的内容。

protected Class? getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

我们看到了一个眼熟的词 —— EnableAutoConfiguration ,也就是说,它实际上返回的就是标注了这个类的所有包。标注了这个类的包不就是@SpringBootApplication吗?

所以我们可以得出结论:它兜兜转转绕了这么多地方,就是为了将启动类所需的所有资源导入。

我们接着往下看,它其中还有这么一条语句,是一条断言:

Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");

这个断言的意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories,我们把这个逻辑反过来想想。如果这个集合不为空,是不是就代表找到了这个spring.factories并且会去加载这个文件中的内容呢?

带着这个疑问,我们首先找到spring.factories这个文件:

图片

可以看到里面包含了很多自动配置属性:

图片

我们可以随便找一个自动配置点进去,比如WebMvcAutoConfiguration

图片

这里放了所有关于WebMvc的配置,如视图解析器、国际化等等。

分析到这里,我们就可以得出一个完整的结论了:

当我们的SpringBoot项目启动的时候,会先导入AutoConfigurationImportSelector,这个类会帮我们选择所有候选的配置,我们需要导入的配置都是SpringBoot帮我们写好的一个一个的配置类,那么这些配置类的位置,存在与META-INF/spring.factories文件中,通过这个文件,Spring可以找到这些配置类的位置,于是去加载其中的配置。

图片

看到这里,可能有些同学会存在疑问,spring.factories中存在那么多的配置,每次启动时都是把它们全量加载吗?这显然是不现实的。

这其实也是我在看源码的时候存在疑问的地方,因为其中有一个注解并不常用,我们点开一个配置类就可以看到。

图片

@ConditionalOnXXX:如果其中的条件都满足,该类才会生效。

所以在加载自动配置类的时候,并不是将spring.factories的配置全量加载进来,而是通过这个注解的判断,如果注解中的类都存在,才会进行加载。

所以就实现了:我们在pom.xml文件中加入stater启动器,SpringBoot自动进行配置。完成开箱即用。

结论

SpringBoot所有自动配置类都是在启动的时候进行扫描并加载,通过spring.factories可以找到自动配置类的路径,但是不是所有存在于spring,factories中的配置都进行加载,而是通过@ConditionalOnClass注解进行判断条件是否成立(只要导入相应的stater,条件就能成立),如果条件成立则加载配置类,否则不加载该配置类。

在这里贴一个我认为的比较容易理解的过程:

  • SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  • 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  • 以前我们需要自己配置的东西 , 自动配置类都帮我们解决了
  • 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  • 它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
  • 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  • 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

摘自https://blog.kuangstudy.com/index.php/archives/630/


图片

约定大于配置

开箱即用的原理说完了,约定大于配置就比较好理解了。其实约定大于配置就是开箱即用中那些自动配置的细节。说的具体点就是: 我们的配置文件(.yml)应该放在哪个目录下,配置文件的命名规范,项目启动时扫描的Bean,组件的默认配置是什么样的(比如SpringMVC的视图解析器)等等等等这一系列的东西,都可以被称为约定 ,下面就来一点一点地说一下SpringBoot中的“约定”。

maven目录结构的约定

我们可以去Spring的官网查看一下官方文档,看看文档中描述的目录结构是怎样的。

Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/. The resulting search order is the following:

  • file:./config/
  • file:./
  • classpath:/config/
  • classpath:/

也就是说,spring的配置文件目录可以放在

  • /config
  • /(根目录)
  • resource/config/
  • resource/

这四个路径从上到下存在优先级关系。

SpringBoot默认配置文件的约定

SpringBoot默认可以加载以下三种配置文件:

  • application.yml
  • application.yaml
  • application.properties

建议使用前两种作为项目的配置文件。

项目启动时扫描包范围的约定

SpringBoot的注解扫描的默认规则是SpringBoot的入口类所在包及其子包。

若入口类所在的包是cn.objectspace.demo那么自动扫描包的范围是cn.objectspace.demo包及其下面的子包,如果service包和dao包不在此范围,则不会自动扫描。


SpringBoot自动配置类如何读取yml配置

从更细节的角度去理解自动配置

上文中我们阐述了一些SpringBoot自动配置的原理,我们是从全局的角度去看自动配置的整个过程。比如从哪个地方开始进行装配流程、如何找到装配的包等。

那么现在将自己的视角贴近SpringBoot,来聊聊application.yml中我们配置的东西,是如何配置到一个个的配置类中的。

yml配置文件中可以配置那些东西

首先要知道这个问题的答案,我们应该习惯springboot的配置方式。在上文中我们阐述了SpringBoot总是将所有的配置都用JavaConfig的形式去呈现出来,这样能够使代码更加优雅。

那么yml中配置的东西,必然是要和这种配置模式去进行联系的,我们在application.yml中配置的东西,通常是一些存在与自动配置类中的属性,那么这些自动配置类,在启动的时候是怎么找到的呢?

如果你还记得上文的描述,那么你可以很明确地知道:spring.factories!没错,就是它,所以这个问题我们似乎得到了答案——只要存在与spring.factories中的,我们都可以在application.yml中进行配置。

当然,这并不意味着不存在其中的我们就不能配置,这些配置类我们是可以进行自定义的,只要我们写了配置类,我们就可以在yml中配置我们需要的属性值,然后在配置类中直接读取这个配置文件,将其映射到配置类的属性上。那么就牵扯出我们的问题了:配置类是如何去读取yml配置文件中的信息的呢?

@ConfigurationProperties

要明白这个问题。我们就首先要去了解这个注解有什么作用。

我们可以自己尝试在application.yml中去定义一些属性,如下:

object: 
  name: Object
  blogurl: blog.objectspace.cn

我们现在自己定义一个类去读取这个文件:

@Component
@ConfigurationProperties(prefix = "object")
public class TestConfig {
    private String name;
    private String blogUrl;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getBlogUrl() {
        return blogUrl;
    }
    public void setBlogUrl(String blogUrl) {
        this.blogUrl = blogUrl;
    }
}

然后我们在测试类中输出一下这个对象:

@SpringBootTest
class SpringbootdemoApplicationTests {
    @Autowired
    TestConfig testConfig;
    @Test
    void contextLoads() {
        System.out.println(testConfig.getName());
        System.out.println(testConfig.getBlogUrl());
    }

}

测试结果:

图片

我们可以看到,在控制台中输出了我们在yml中配置的属性值,但是这些值我们没有在任何地方显式地对这个对象进行注入。

所以@ConfigurationProperties这个注解,可以将yml文件中写好的值注入到我们类的属性中。

明白了它的作用,就能明白自动配置类工作的原理了。

我们依旧是选取SpringMVC的自动配置类,我们来看看其中有些什么东西。

图片

点击任意一个*Properties类中,look一下其中的内容:

图片

看到这里相信所有人都明白了,我们就拿mvc配置来举例。

图片

我们在yml中配置的date-format,就可以通过@ConfigurationProperties映射到类中的dateFormat中,然后在通过自动配置类,将这些属性配置到配置类中。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2908

    浏览量

    103146
  • 开发
    +关注

    关注

    0

    文章

    351

    浏览量

    40648
  • 框架
    +关注

    关注

    0

    文章

    344

    浏览量

    17114
  • spring
    +关注

    关注

    0

    文章

    333

    浏览量

    14183
收藏 人收藏

    评论

    相关推荐

    [转帖]当你扛不住的时候就读一遍

      当你扛不住的时候就读一遍1.不求与人相比,但求超越自己,要哭就哭出激动的泪水,要笑就笑出成长的性格。  2.与其用泪水悔恨今天,不如用汗水拼. 搏今天。  3.当眼泪流尽的时候
    发表于 05-24 11:58

    款24转12直流 输出2A的设计图.我只听说一遍不知道记错...

    `-.-我朋友只跟我说了一遍而已 我不知道我有没记错 求大神啊 !!!`
    发表于 07-14 11:51

    个人认为51汇编至少过一遍

    个人认为51汇编至少过一遍。现在网上百分之九十的单片机资料都是C 语言的,都适合初学者入门,我觉得这个不错的。现在也越来越多的发烧友认为没必要再学汇编,直接学C语言,这当然没错,可惜,如果你连单片机
    发表于 08-21 08:22

    主程序执行一遍后单片机锁存器怎么变化?

    例如点亮数码管的程序最后行死循环去掉的话锁存器是保持状态使数码管亮着还是执行一遍主函数就灭了?#include***it dula=P2^6;***it wela=P2^7;void main
    发表于 10-19 18:25

    刚学STM32,按照个网上找的LED程序,自己写了一遍,可是...

    刚学STM32,按照个网上找的LED程序,自己写了一遍,可是无法编译!!求大神帮忙解决一下,感激不尽。下面是我写的程序。
    发表于 11-04 02:33

    只是为了积分,刷一遍主题

    只是为了积分,刷一遍主题
    发表于 10-11 11:14

    已经跟着入门书籍学了一遍LabVIEW基础知识,现在该怎么练手呢

    小白刚起步,过了一遍基本知识后不知如何开展下步,大神们给指导个方向,或者下步需要用的学习资料也可以
    发表于 05-04 16:30

    IAP15W413AS灌程序要先用串口烧录一遍

    IAP15W413AS灌程序要先用串口烧录一遍然后在烧录软件中选择下次用485烧录,为何不能直接用485工具烧录?是啥原因啊!!!
    发表于 06-26 18:35

    玩转电机自动化装配

    的需求量,也带动了装备制造业的发展,10年前的电机生产线多为半自动生产线,以人工取放料,设备自动装配为主。电机通用性标准半自动工作台电机通用性标准半
    发表于 10-29 17:16

    6678点击ccml文件debug时当前的out文件会给所有的core都下载一遍

    我的板子是EVM6678L的开发板,ccs5.3版本,现在有个问题,我开始还好好的,突然在次右键点击ccml文件debug时,当前的out文件会给所有的core都下载一遍,在debug之前,我已经选择了只是用core0了,不知道为什么?望达人解答
    发表于 12-28 11:01

    怎么让液晶把每种颜色都显示一遍

    原子哥哥,我想让液晶吧每种颜色都显示一遍,可是有很多种颜色为显示出来?#头文件int main( void ){u16i ; 初始化; do {LCD_Clear( i );i++;delay_ms
    发表于 03-04 06:35

    请问在bootloader里面已经包含的配置在app里面还需要在配置一遍吗?

    请问大家在bootloader里面已经包含的配置在app里面还需要在配置一遍么。。如果在app里面的配置和bootloader里面不样呢。。望高手给予帮助。。
    发表于 04-22 03:26

    为什么任哲VC上的移植我只能运行一遍

    只显示一遍MY,不是循环显示。例12-2
    发表于 11-04 03:47

    盘点各类自动控制的原理图

    盘点各类自动控制原理图
    发表于 03-16 06:50

    深度盘点一遍自动装配原理(上)

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
    的头像 发表于 04-07 11:18 568次阅读
    <b class='flag-5'>深度</b><b class='flag-5'>盘点</b><b class='flag-5'>一遍</b><b class='flag-5'>自动</b><b class='flag-5'>装配</b>原理(上)