首页 > 编程语言 > 详细

SpringBoot-03-原理

时间:2020-09-19 13:12:19      阅读:55      评论:0      收藏:0      [点我收藏+]

1、SpringBoot依赖管理

  • spring-boot-starter-parent父依赖项目管理

    <parent>
       <artifactId>spring-boot-starter-parent</artifactId>
       <groupId>org.springframework.boot</groupId>
       <version>2.1.7.RELEASE</version>
    </parent>
    • 查看spring-boot-starter-parent底层源文件

      发现存在另外一个父依赖spring-boot-dependencies,核心代码如下:

    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-dependencies</artifactId>
       <version>2.1.7.RELEASE</version>
       <relativePath>../../spring-boot-dependencies</relativePath>
    </parent>
    ...
    • 查看spring-boot-dependencies底层源文件

      • 该文件通过 properties 标签对一些常用技术框架的依赖文件进行了统一的版本号管理

      • 所以通过pom.xml引入依赖文件时,如果引入的依赖文件被spring-boot-starter-parent所管理,则不需要引入版本号,否则需要使用 version 标签指定版本号

      <properties>
         <activemq.version>5.15.9</activemq.version>
        ...
         <spring.version>5.1.9.RELEASE</spring.version>
         <spring-amqp.version>2.1.8.RELEASE</spring-amqp.version>
         <spring-batch.version>4.1.2.RELEASE</spring-batch.version>
         <spring-cloud-connectors.version>2.0.6.RELEASE</spring-cloud-connectors.version>
         <spring-data-releasetrain.version>Lovelace-SR10</spring-data-releasetrain.version>
         <spring-framework.version>${spring.version}</spring-framework.version>
         <spring-hateoas.version>0.25.2.RELEASE</spring-hateoas.version>
         <spring-integration.version>5.1.7.RELEASE</spring-integration.version>
         <spring-kafka.version>2.2.8.RELEASE</spring-kafka.version>
         <spring-ldap.version>2.3.2.RELEASE</spring-ldap.version>
         <spring-plugin.version>1.2.0.RELEASE</spring-plugin.version>
         <spring-restdocs.version>2.0.3.RELEASE</spring-restdocs.version>
         <spring-retry.version>1.2.4.RELEASE</spring-retry.version>
         <spring-security.version>5.1.6.RELEASE</spring-security.version>
         <spring-session-bom.version>Bean-SR7</spring-session-bom.version>
         <spring-ws.version>3.0.7.RELEASE</spring-ws.version>
         <sqlite-jdbc.version>3.25.2</sqlite-jdbc.version>
         <statsd-client.version>3.1.0</statsd-client.version>
         <sun-mail.version>${javax-mail.version}</sun-mail.version>
         <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
         <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
         <thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
         <thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
         <thymeleaf-layout-dialect.version>2.3.0</thymeleaf-layout-dialect.version>
         <tomcat.version>9.0.22</tomcat.version>
         <unboundid-ldapsdk.version>4.0.11</unboundid-ldapsdk.version>
         <undertow.version>2.0.23.Final</undertow.version>
      ...
      </properties>

 

  • spring-boot-starter-webWeb场景依赖启动器

    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    • 查看spring-boot-starter-web依赖文件源码

      • 提供web依赖开发场景所需的底层所有依赖环境

      <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>
             <version>2.1.7.RELEASE</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-json</artifactId>
             <version>2.1.7.RELEASE</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-tomcat</artifactId>
             <version>2.1.7.RELEASE</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.hibernate.validator</groupId>
             <artifactId>hibernate-validator</artifactId>
             <version>6.0.17.Final</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
             <version>5.1.9.RELEASE</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>5.1.9.RELEASE</version>
             <scope>compile</scope>
         </dependency>
      </dependencies>

 

2、SpringBoot自动配置

自动配置由以下注解决定:

@SpringBootApplication:标注这个类是一个SpringBoot的应用
?
1@SpringBootConfiguration:表示这是个SpringBoot配置类
1.1@Configuration
1.1.1@Component
?
2@EnableAutoConfiguration:开启自动配置功能
2.1@AutoConfigurationPackage
2.1.1@Import(AutoConfigurationPackages.Registrar.class):向容器导入注册的所有组件,导入的组件由Resgistrar决定
2.2@Import(AutoConfigurationImportSelector.class)
?
3@ComponentScan:将指定包中的注解类自动装配到Spring的Bean容器中
  • @SpringBootApplication

    • 标注这个类是一个SpringBoot的应用

    • 能够扫描Spring组件并自动配置SpringBoot

    • 自动配置的原理?

      @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 {
        ......
      }

     

    • 1、@SpringBootConfiguration:表示这是个SpringBoot配置类

      • 注解源码的核心代码如下:

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Configuration
      public @interface SpringBootConfiguration {
      }
      • 发现存在一个@Configuration注解,说明@SpringBootConfiguration和@Configuration注解的作用一样,标识一个可以被组件扫描器扫描的配置类。

     

    • 2、@EnableAutoConfiguration:开启自动配置功能

      • 注解源码的核心代码如下:

        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Inherited
        @AutoConfigurationPackage
        @Import({AutoConfigurationImportSelector.class})
        public @interface EnableAutoConfiguration {
          String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
        ?
          Class<?>[] exclude() default {};
        ?
           String[] excludeName() default {};
        }
      • 发现@EnableAutoConfiguration由@AutoConfigurationPackage和@Import()组合而成

        • @AutoConfigurationPackage的源码

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Inherited
          @Import({Registrar.class})
          public @interface AutoConfigurationPackage {
          }
          • @Import({Registrar.class})

            • 作用:

              向容器导入注册的所有组件,导入的组件由Resgistrar决定

            • Registrar类的源码的核心代码如下:

              static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
                 Registrar() {
                }
              ?
                 /*
                     使用debug模式启动项目,
               AutoConfigurationPackages.PackageImport(metadata)).getPackageName(),这个部分获取的是项目主程序启动类所在的目录
                     */
                 public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                     AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
                }
                ...
              }
        • 总结:@AutoConfigurationPackage的主要作用是获取项目主程序启动类所在根目录,从而指定后续组件扫描器要扫描的包位置

         

        • @Import({AutoConfigurationImportSelector.class})

          • 查看AutoConfigurationImportSelector类的getAutoConfigurationEntry()方法

            protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
               if (!this.isEnabled(annotationMetadata)) {
                   return EMPTY_ENTRY;
              } else {
                   AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                   
                   /*
                   获取SpringBoot提供的后续自动配置类XXXAutoConfiguration
                   */
                   List<String> configurations =
                       /*
                       该方法的主要作用是
                       从SpringBoot提供的自动配置依赖环境META-INF/spring.factories
                       文件中获取所有的候选自动配置类XXXAutoConfiguration
                  */
                       this.getCandidateConfigurations(annotationMetadata, attributes);
                   
                   configurations = this.removeDuplicates(configurations);
                   Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                   this.checkExcludedClasses(configurations, exclusions);
                   configurations.removeAll(exclusions);
                   
                   /*
                   筛选并过滤当前应用环境下需要的自动配置类XXXAutoConfiguration
                   */
                   configurations = this.filter(configurations, autoConfigurationMetadata);
                   /*
                   该方法的主要作用是
                   对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选 出最终符合当前项目运行环境对应的自动配置类
                   */
                   this.fireAutoConfigurationImportEvents(configurations, exclusions);
                   
                   return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
              }
            }
        • 总结:@Import({AutoConfigurationImportSelector.class})的主要作用是筛选出当前项目需要启动的自动配置类,从而实现当前项目运行所需的自动配置环境

 

  • 3、@ComponentScan():组件包扫描器

    将指定包中的注解类自动装配到Spring的Bean容器中

    具体扫描的包的根路径由SpringBoot项目主程序启动类所在包位置决定

 

  • 总结:在定义项目包结构时,要求定义的包结构要非常规范,保证定义的类能够被组件扫描器扫描

    • 项目主程序启动类要定义在最外层的根目录位置

    • 然后在根目录位置内部建立子包和类进行业务开发

 

3、SpringBoot执行流程

在主程序启动类中的main()方法中,执行SpringApplication.run()即可启动整个SpringBoot程序,具体的执行流程是什么呢?

技术分享图片

 

  • run()方法内部源码的核心代码:

    /*
    SpringApplication.run()的方法内部执行了两个操作
    1、SpringApplication的初始化
    2、调用run()启动项目
    */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
       return (new SpringApplication(primarySources)).run(args);
    }
    • 1、SpringApplication的初始化

      SpringApplication实例对象初始化的源码信息的核心代码:

      public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
         this.sources = new LinkedHashSet();
         this.bannerMode = Mode.CONSOLE;
         this.logStartupInfo = true;
         this.addCommandLineProperties = true;
         this.addConversionService = true;
         this.headless = true;
         this.registerShutdownHook = true;
         this.additionalProfiles = new HashSet();
         this.isCustomEnvironment = false;
         this.resourceLoader = resourceLoader;
         Assert.notNull(primarySources, "PrimarySources must not be null");
         
         this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
         
         this.webApplicationType = WebApplicationType.deduceFromClasspath();
         this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
         this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
         
         this.mainApplicationClass = this.deduceMainApplicationClass();
      }

      从上述源码可以看出,SpringApplication的初始化过程分为4部分

      • 1、this.webApplicationType = WebApplicationType.deduceFromClasspath();

        • 判断当前webApplicationType 应用的类型

        • deduceFromClasspath()用于查看Classpath路径下是否存在某个特征类,从而判断类型是SERVLET应用(传统MVC应用)还是REACTIVE应用(WebFlux交互式应用)

      • 2、this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

        • 用于设置SpringApplication应用的初始化器

        • 在初始化器设置过程中会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories文件中获取所有可用的应用初始化器类ApplicationContextInitializer

      • 3、this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

        • 用于设置SpringApplication应用的监听器

        • 在初始化器设置过程中会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories文件中获取所有可用的监听器类ApplicationListener

      • 4、this.mainApplicationClass = this.deduceMainApplicationClass();

        • 用于推断并设置项目main()方法启动的主程序启动类

    • 2、调用run()启动项目

      run(args)方法执行的项目初始化启动过程的核心代码:

      public ConfigurableApplicationContext run(String... args) {
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         ConfigurableApplicationContext context = null;
         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
         this.configureHeadlessProperty();
         
         //(1)获取SpringApplication初始化的SpringApplicationRunListener运行监听器并运行
         SpringApplicationRunListeners listeners = this.getRunListeners(args);
         listeners.starting();
      ?
         Collection exceptionReporters;
         try {
             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
             
             //(2)项目运行环境的预配置
             ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
             //排除一些不需要的运行环境
             this.configureIgnoreBeanInfo(environment);
             
             Banner printedBanner = this.printBanner(environment);
             
             /*
             (3)项目应用上下文ApplicationContext的预配置
            创建应用上下文环境ApplicationContext
            使用之前初始化设置的context(应用上下文环境)、environment、listener、applicationArguments(项目参数)、printedBanner(项目图标信息)进行上下文的组装配置,并刷新配置
             */
             context = this.createApplicationContext();
             exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
             this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
             this.refreshContext(context);
             this.afterRefresh(context, applicationArguments);
             
             stopWatch.stop();
             if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
      ?
             //(4)由项目运行监听器启动配置好的应用上下文ApplicationContext
             listeners.started(context);
             
             //(5)调用上下文中、中配置的程序执行器XXXRunner,使得在项目启动完成后立即执行一些特定程序
             this.callRunners(context, applicationArguments);
             
        } catch (Throwable var10) {
             this.handleRunFailure(context, var10, exceptionReporters, listeners);
             throw new IllegalStateException(var10);
        }
      ?
         try {
             
             //(6)由项目运行监听器持续运行配置好的应用上下文
             listeners.running(context);
             return context;
        } catch (Throwable var9) {
             this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
             throw new IllegalStateException(var9);
        }
      }

 

SpringBoot-03-原理

原文:https://www.cnblogs.com/LittleSkinny/p/13695341.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!