zoukankan      html  css  js  c++  java
  • 带你一行一行分析SpringBoot原码解析

    一.说明

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

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

    二.原码分析

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

     只有一个配置文件和一个启动类。

    配置文件中只配了一个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,也是比较复杂的,点进去,如下图:

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

    接下来就是最重要的注解了,@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,点进去,如下图:

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

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

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

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

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

     继续进入loadSpringFactories,

     图中有个FACTORIES_RESOURCE_LOCATION,点进去,

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

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

     找到spring-boot-starter,点进去

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

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

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

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

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

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

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

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

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

     进入environmentPrepared,表示添加监听:

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

    multicastEvent->

    multicastEvent->

    multicastEvent->

    invokeListener(listener, event))->

    doInvokeListener——>如图:

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

     进入实现方法:

     再按下顺序往下走:

    onApplicationEnvironmentPreparedEvent ->

    postProcessEnvironment->如图:

    再进入postProcessEnvironment的实现类:ConfigFileApplicationListener

     实现方法如下:

     再进入addPropertySources方法:

     在进入load方法:

     再进入load方法:

     再进入load方法:

     点击getFileExtensions()进入接口,

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

    重新进入load方法:

     进入loadForFileExtension方法:

     

     再进入load方法:

     再进入loadDocuments方法:

     再进入load接口方法:

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

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

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

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

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

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

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

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

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

  • 相关阅读:
    STM32中管脚利用
    阻抗匹配
    无功功率与补偿
    宏的灵活应用例子
    串口接入检测与串口命令解析
    16 款最流行的JavaScript 框架
    不可错过的10个超棒jQuery表单操作代码片段
    20 个用于处理页面滚动效果的 jQuery 插件
    11 个最佳 jQuery 滚动条插件
    jQuery的搜索关键词自动匹配插件
  • 原文地址:https://www.cnblogs.com/xyzyj/p/13635280.html
Copyright © 2011-2022 走看看