当前位置: 首页 > article >正文

JavaWeb后端基础(8)spring原理

三部分:配置优先级 Bean的管理 剖析Springboot的底层原理

配置优先级

配置文件优先级排名(从高到低):

  1. properties配置文件

  2. yml配置文件

  3. yaml配置文件

虽然springboot支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置。(yml是主流) 

在SpringBoot项目当中除了以上3种配置文件外,SpringBoot为了增强程序的扩展性,除了支持配置文件的配置方式以外,还支持另外两种常见的配置方式:

Java系统属性配置 (格式: -Dkey=value)

命令行参数 (格式:--key=value)

命令行参数的优先级时最高的,同时配置的情况下,命令行参数的配置项生效。

五种配置方式的优先级: 命令行参数 > 系统属性参数 > properties参数 > yml参数 > yaml参数

Bean的管理

在前面的课程当中,我们已经讲过了我们可以通过Spring当中提供的注解@Component以及它的三个衍生注解(@Controller、@Service、@Repository)来声明IOC容器中的bean对象,同时我们也学习了如何为应用程序注入运行时所需要依赖的bean对象,也就是依赖注入DI。

Bean的作用域

作用域说明
singleton容器内同名称的bean只有一个实例(单例)(默认)
prototype每次使用该bean时会创建新的实例(非单例)
request每个请求范围内会创建新的实例(web环境中,了解)
session每个会话范围内会创建新的实例(web环境中,了解)
application每个应用范围内会创建新的实例(web环境中,了解)

可以借助Spring中的@Scope注解来进行配置作用域

  • IOC容器中的bean默认使用的作用域:singleton (单例)

  • 默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)

  • prototype的bean,每一次使用该bean的时候都会创建一个新的实例

  • 实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性

第三方Bean 

在我们项目开发当中,如果这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的,那么此时我们是无法使用 @Component 及其衍生注解来声明bean的,此时就需要使用@Bean注解来声明bean 了。

  • 在启动类中直接声明这个Bean。比如:我们可以将我们之前使用的阿里云OSS操作的工具类,基于@Bean注解的方式来声明Bean。

  • 若要管理的第三方 bean 对象,建议对这些bean进行集中分类配置,可以通过 @Configuration 注解声明一个配置类。【推荐】

  • 通过@Bean注解的name 或 value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。

  • 如果第三方bean需要依赖其他bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。

 SpringBoot原理

通过 SpringBoot来简化Spring框架的开发(是简化不是替代)。我们直接基于SpringBoot来构建Java项目,会让我们的项目开发更加简单,更加快捷。

SpringBoot框架之所以使用起来更简单更快捷,是因为SpringBoot框架底层提供了两个非常重要的功能:一个是起步依赖,一个是自动配置。

通过SpringBoot所提供的起步依赖,就可以大大的简化pom文件当中依赖的配置,从而解决了Spring框架当中依赖配置繁琐的问题。

通过自动配置的功能就可以大大的简化框架在使用时bean的声明以及bean的配置。我们只需要引入程序开发时所需要的起步依赖,项目开发时所用到常见的配置都已经有了,我们直接使用就可以了。

起步依赖

当我们引入了 spring-boot-starter-web 之后,maven会通过依赖传递特性,将web开发所需的常见依赖都传递下来。

自动配置

SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

实现方案

1、@ComponentScan组件扫描

@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfigApplication.class, args);
    }
}

如果采用这种方式来完成自动配置,那我们进行项目开发时,当需要引入大量的第三方的依赖,就需要在启动类上配置N多要扫描的包,这种方式会很繁琐。而且这种大面积的扫描性能也比较低。SpringBoot中并没有采用以上这种方案。

2、@Import导入

  • 导入形式主要有以下几种:

    • 导入普通类

    • 导入配置类

    • 导入ImportSelector接口实现类

      //1
      @Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
      @SpringBootApplication
      public class SpringbootWebConfigApplication {
          public static void main(String[] args) {
              SpringApplication.run(SpringbootWebConfigApplication.class, args);
          }
      }
      
      
      //2.1配置类
      @Configuration
      public class HeaderConfig {
          @Bean
          public HeaderParser headerParser(){
              return new HeaderParser();
          }
      
          @Bean
          public HeaderGenerator headerGenerator(){
              return new HeaderGenerator();
          }
      }
      
      //2.2启动类
      @Import(HeaderConfig.class) //导入配置类
      @SpringBootApplication
      public class SpringbootWebConfig2Application {
          public static void main(String[] args) {
              SpringApplication.run(SpringbootWebConfig2Application.class, args);
          }
      }
      
      //3.1接口实现类
      public class MyImportSelector implements ImportSelector {
          public String[] selectImports(AnnotationMetadata importingClassMetadata) {
              //返回值字符串数组(数组中封装了全限定名称的类)
              return new String[]{"com.example.HeaderConfig"};
          }
      }
      //启动类
      @Import(MyImportSelector.class) //导入ImportSelector接口实现类
      @SpringBootApplication
      public class SpringbootWebConfig2Application {
          public static void main(String[] args) {
              SpringApplication.run(SpringbootWebConfig2Application.class, args);
          }
      }
      
      

4、使用第三方依赖提供的 @EnableXxxxx注解 

  • 第三方依赖中提供的注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
    public @interface EnableHeaderConfig { 
    }
  • 在使用时只需在启动类上加上@EnableXxxxx注解即可

    @EnableHeaderConfig  //使用第三方依赖提供的Enable开头的注解
    @SpringBootApplication
    public class SpringbootWebConfig2Application {
        public static void main(String[] args) {
            SpringApplication.run(SpringbootWebConfig2Application.class, args);
        }
    }

自动配置原理 

自动配置原理源码入口就是 @SpringBootApplication 注解,在这个注解中封装了3个注解,分别是                     

  • @SpringBootConfiguration

    • 声明当前类是一个配置类

  • @ComponentScan

    • 进行组件扫描(SpringBoot中默认扫描的是启动类所在的当前包及其子包)

  • @EnableAutoConfiguration

    • 封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)

 在实现类重写的selectImports()方法,读取当前项目下所有依赖jar包中META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。

 当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息(类的全限定名)封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。

Conditional

我们在跟踪SpringBoot自动配置的源码的时候,在自动配置类声明bean的时候,除了在方法上加了一个@Bean注解以外,还会经常用到一个注解,就是以Conditional开头的这一类的注解。以Conditional开头的这些注解都是条件装配的注解。

@Conditional注解:

  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。

  • 位置:方法、类

  • @Conditional本身是一个父注解,派生出大量的子注解:

    • @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。

    • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。

    • @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器

重点总结

自动配置的核心就在@SpringBootApplication注解上,SpringBootApplication这个注解底层包含了3个注解,分别是:

  • @SpringBootConfiguration

  • @ComponentScan

  • @EnableAutoConfiguration

@EnableAutoConfiguration这个注解才是自动配置的核心。

  • 它封装了一个@Import注解,Import注解里面指定了一个ImportSelector接口的实现类。

  • 在这个实现类中,重写了ImportSelector接口中的selectImports()方法。

  • 而selectImports()方法中会去读取两份配置文件,并将配置文件中定义的配置类做为selectImports()方法的返回值返回,返回值代表的就是需要将哪些类交给Spring的IOC容器进行管理。

  • 那么所有自动配置类的中声明的bean都会加载到Spring的IOC容器中吗? 其实并不会,因为这些配置类中在声明bean时,通常都会添加@Conditional开头的注解,这个注解就是进行条件装配。而Spring会根据Conditional注解有选择性的进行bean的创建。

  • @Enable 开头的注解底层,它就封装了一个注解 import 注解,它里面指定了一个类,是 ImportSelector 接口的实现类。在实现类当中,我们需要去实现 ImportSelector 接口当中的一个方法 selectImports 这个方法。这个方法的返回值代表的就是我需要将哪些类交给 spring 的 IOC容器进行管理。

  • 此时它会去读取两份配置文件,一份儿是 spring.factories,另外一份儿是 autoConfiguration.imports。而在 autoConfiguration.imports 这份儿文件当中,它就会去配置大量的自动配置的类。

  • 而前面我们也提到过这些所有的自动配置类当中,所有的 bean都会加载到 spring 的 IOC 容器当中吗?其实并不会,因为这些配置类当中,在声明 bean 的时候,通常会加上这么一类@Conditional 开头的注解。这个注解就是进行条件装配。所以SpringBoot非常的智能,它会根据 @Conditional 注解来进行条件装配。只有条件成立,它才会声明这个bean,才会将这个 bean 交给 IOC 容器管理。


http://www.kler.cn/a/578316.html

相关文章:

  • 炒菜的基本逻辑?
  • Codeforces Round 258 (Div. 2) E. Devu and Flowers 生成函数
  • Phi-4-multimodal:图、文、音频统一的多模态大模型架构、训练方法、数据细节
  • 智源开源多模态向量模型BGE-VL:多模态检索新突破
  • google s2部分浅讲
  • JVM - 3.垃圾回收
  • 物联网(Internet of Things, IoT)中的网络层简介
  • Eclipse 查看 JAVA SE 23 官方API 源代码
  • 为什么 MySQL InnoDB 的 Repeatable Read 可以阻止幻读?
  • 【JavaEE进阶】Spring AOP详解
  • preloaded-classes裁剪
  • 工业单板电脑在电商物流中心的应用案例
  • Java多线程与高并发专题——什么是阻塞队列?
  • Windows CMD 命令大全(综合开发整理版)
  • 【编译器】VSCODE搭建ESP32
  • C++:dp3则
  • 数据库---1.0
  • 基于SpringBoot的餐厅点餐管理系统设计与实现(源码+SQL脚本+LW+部署讲解等)
  • 【redis】全局命令set、get、keys
  • typora高亮方案+鼠标侧键一键改色