springboot yml怎么建常量_带你一行一行分析SpringBoot原码解析

一.说明

 1.本次源码解析是基于2.3.3.RELEASE版本的

 2.本文主要分析Spring的自动配置

回到顶部

二.原码分析

1.创建一个普通的springboot项目如下:

06e9ebc04b7aa97c4c42784f990ce13e.png 只有一个配置文件和一个启动类。

0f5180f7ebe31a295594287475bbf920.png

配置文件中只配了一个redis,配置其他组件都行,这里以redis为例展开说明自动注入。

2.打开启动类

对于springboot来说,最强大的地方就是没有复杂的配置文件,创建springboot后只有一个启动类,那就从启动类入手,Ctrl + 左键 点击

@SpringBootApplication 注解,进入如下所示:
@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 {

  可以看到,

@SpringBootApplication注解上面又有七个注解,其实,这种包含了很多注解的注解就是组合注解。前四个是元注解(在JDK 1.5中提供了4个标准的用来对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)),我们先分析前四个注解:

  • @Target :描述注解的使用范围,括号里有个ElementType.TYPE,点进去,如下:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。

  • @Retention 同理,点击去 RetentionPolicy类,它的主要作用是:用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。

public enum RetentionPolicy {

    SOURCE,    // 源文件保留
    CLASS,       // 编译期保留,默认值
    RUNTIME   // 运行期保留,可通过反射去获取注解信息
  • @Documented ,它的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息

  • @Inherited,这是个比较重要的注解,它表示注解会被子类自动继承。

接下来就剩三个注解了,其中@EnableAutoConfiguration是最核心的注解,我们放到最后面说,先说其他两个注解:

  •     @SpringBootConfiguration :它的作用就是:继承自@Configuration,二者功能也一致,标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。可以点击去看下,如下:其实,@Configuration和 @SpringBootConfiguration 是具有相同功能的。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@ComponentScan,也是比较复杂的,点进去,如下图:

0768084130e3729b1c350560eb6e45c8.png

 这也就是为什么,所有的代码都要放在启动类所在的包及子包里面。

接下来就是最重要的注解了,@EnableAutoConfiguration ,先点进去,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited   
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};
}

前四个注解就不说了,

    @AutoConfigurationPackage的作用是导入自定义的类的,

    @Import(AutoConfigurationImportSelector.class)是导入框架本身的一些类的

  在这里,这两个注解仅仅是找到需要导入的类,并没有实例化,实例化依然需要spring容器去做。

先看@AutoConfigurationPackage,点进去,如下图:

033805640de0f44b1714fd09f4fc5fd6.png

有个Registrar,点进去,如下图:

9554069f01e63743c7c5228ea242068c.png

 从方法名可以看出是个注册的方法,打上断点,启动,当代码停住后,Alt + F8,查看new PackageImports(metadata).getPackageNames().toArray(new String[0])的值,如下图:

8d8532a4aae219c478d19dac42b633fe.png

 从上图可以看出,这个方法其实就是就是把自定义包下的类扫描并注册到容器中。

再看@Import(AutoConfigurationImportSelector.class),它是注入框架本身使用的和自动配置相关的类,点击去,找到getCandidateConfigurations方法。

2b4c0458a2fa068d6886280661540193.png

 注释的意思的是;找到可能的自动配置的类名,进入loadFactoryNames方法,

de06ab325f04469f14fdd52fba99d2d6.png

 继续进入loadSpringFactories,

64da893b203beb4709c8e606bb7c9c04.png

 图中有个FACTORIES_RESOURCE_LOCATION,点进去,

969501343266c5b6b1c8d592593a3baf.png

 原来是一个文件,也就是说,springboot框架本省要导入的类就在这个文件中,那么这个spring.factories在哪呢?

接下来打开pom文件,找到spring-boot-starter-web,点进去,

fb888c4d684fd7cfc652e598d1e91bc7.png

 找到spring-boot-starter,点进去

7872f4f1c7b506af1b171fe29e483c34.png

 就能发现有一个spring-boot-autoconfigure,表示自动配置,如图。

0da674a8fe054cb308fb40982bd0c2fd.png

打开pom文件引入的包,在工程窗口,如下图:

aecdc463605957bf70957f5ebd6b3d32.png

 找到:spring-boot-autoconfigure,如下图:

3a76b3f14b16260d030c45763e7d1a12.png

 发现有一个spring.properties文件,打开:

0b3abcb24581f43f5147127d98aa7258.png

 发现,类似于redis这种组件所对应的类就在这个文件中。

到此为止,springboot只是将可能用到的类加载进来了,但是仅仅知识加载了类名,怎么能根据我们在yml文件中的配置来使用呢?也就是说,springboot怎么能知道我们要使用哪些类,不使用哪些类呢,比如我们现在要是用redis,首先我们需要在yml文件中配置redis的连接信息,如下图:

5d88255db2256335e395d258fe2de5fe.png

 想到这里,我们就应该想到,springboo肯定是通过加载这个yml文件开读取的,接下来跟原码:

打开启动类,进入run方法,只要是run方法,就一直往下走,直到org.springframework.context.ConfigurableApplicationContext这个方法,如下图:

06eef39c0db711b4bd961fdf95d83081.png

 进入:prepareEnvironment方法这个方法表示环境的准备,如下图;

e47594fe25634a3572a5c467e064181c.png

 进入environmentPrepared,表示添加监听:

2cb00791a59d1f13843c48dc4d22f337.png

 进入environmentPrepared方法,表示初始化:继续往下走,方法顺序为

multicastEvent->

multicastEvent->

multicastEvent->

invokeListener(listener, event))->

doInvokeListener——>如图:

fb4315f02fc5e76c80782a874e9d69e6.png

 进入onApplicationEvent接口的配置文件实现类  ConfigFileApplicationListener

5189f164e6d59bd4e6e7097c98336639.png

 进入实现方法:

ebfedb4d78a8eee4271ae613d2b689d9.png

 再按下顺序往下走:

onApplicationEnvironmentPreparedEvent ->

postProcessEnvironment->如图:

再进入postProcessEnvironment的实现类:ConfigFileApplicationListener

d1686372f0bfbcc5b95813a338e9e1e5.png

 实现方法如下:

8eb5055ebe86abfdc2950cf03886504d.png

 再进入addPropertySources方法:

1e78ccdaeab767bcdc682237595e45fd.png

 在进入load方法:

e6b4096422810d5816172806347772cd.png

 再进入load方法:

542ac4b0010ddc3bdad13bfb01552e71.png

 再进入load方法:

9438e75431bf0267ecb1005c0df6c2a2.png

 点击getFileExtensions()进入接口,

1c97d397fc67fdbb1812b8082db7faed.png

 这个接口有两个实现类,分别是properties和yml,太熟悉了,这不就是配置文件吗,这个接口其实就是配置文件的扩展名,

重新进入load方法:

fe642d7ec3303b7b728d7ea9663490db.png

 进入loadForFileExtension方法:

 f6b24d0b7a0d5691fb6a6ad870c979b5.png

 再进入load方法:

788e73fef4864f03a2b3fc2a0ef46ef0.png

 再进入loadDocuments方法:

abf80167f4026c6723cb7d810036ecd1.png

 再进入load接口方法:

74736b5932f194a526180377ae48d3b7.png

 发现又有两个实现类,进入yml的实现类:

4b89732b9ca624d0a928bee1933371c5.png

 ····················终于完了,这是最后一个方法了,在犯法最后一行打上断点:启动:

5d58bf140b2eab7c02c1cc2b023c4895.png

 把配置文件中的所有信息都加载进来了。

但是。。。,这也只是把配置文件加载进来了,那么redis怎么起作用呢?

打开前面说的spring.properties文件找到  RedisAutoConfiguration类,进入这个类,如下

2fa15cf5d1f7943fd7fc0f5a4dfbf9e7.png

 要使redis起作用,图中的四个注解不可少,进入注解括号中的RedisProperties类:

f9b85a65c0c62823920e7272d7532baf.png

 发现,又是熟悉的感觉,这不就对饮配置文件中的属性吗?

再看RedisTemplate template = new RedisTemplate<>(); 这行代码,直接就new了一个对象,这就是创建了一个RedisTemplate,因此,我们就可以使用RedisTemplate来操作redis了。

所以springboot,也就是通过这种方式进行类的自动装配的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值