四、SpringBoot

100 阅读9分钟

一,SpringBoot介绍

Spring Boot 是 Spring 生态中的一款 快速开发框架,旨在简化基于 Spring 的应用搭建和配置流程。它通过 约定优于配置 的设计理念,提供开箱即用的功能,让开发者专注于业务逻辑而非复杂的配置。Spring Boot 常被用于构建独立运行、生产级的微服务或单体应用。


核心特性

  1. 自动配置(Auto-Configuration)
    • 根据项目依赖(如 JDBC、Web、Security)自动配置 Spring 应用,无需手动编写 XML 或 Java 配置。
    • 例如:引入 spring-boot-starter-web 后,自动配置 Spring MVC 和嵌入式 Tomcat。
  2. 内嵌服务器(Embedded Server)
    • 默认集成 Tomcat、Jetty 或 Undertow,应用可直接打包为 可执行 JAR,无需部署到外部 Web 服务器。
    • 通过一行命令即可启动:java -jar app.jar
  3. Starter 依赖(Starters)
    • 提供预定义的依赖集合(如 spring-boot-starter-data-jpaspring-boot-starter-security),简化依赖管理。
    • 避免手动解决库版本冲突问题。
  4. 生产就绪(Production-Ready)
    • 集成 Actuator 模块,提供监控端点(如健康检查、指标收集、日志管理)。
    • 支持通过 HTTP 或 JMX 监控应用状态。
  5. 外部化配置
    • 支持通过 application.propertiesapplication.yml 文件灵活配置应用参数。
    • 支持多环境配置(如 application-dev.ymlapplication-prod.yml)。
  6. 无代码生成与零 XML 配置
    • 完全基于 Java 注解和约定,无需生成代码或编写 XML。

二,快速入门

image

image

image

image


Spring程序与SpringBoot程序对比 image

三,SpringBoot项目快速启动

  1. 对SpringBoot项目打包(执行Maven构建指令package)

  2. 执行启动命令

    java -jar springboot.jar
    

    image


切换Tomcat服务器为Jetty服务器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
<exclusions>
    <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
</exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

四,起步依赖

Spring Boot 的 起步依赖(Starter Dependencies) 是其简化项目依赖管理的核心机制之一。它通过预定义的依赖集合和版本管理,让开发者能够快速引入特定功能所需的所有库,避免手动处理依赖冲突和版本兼容性问题。

起步依赖的核心思想

  1. 约定优于配置
    • 每个 Starter 对应一个特定功能(如 Web 开发、数据库访问、安全等),一键引入相关依赖。
    • 开发者无需手动查找和配置依赖的版本,Spring Boot 自动确保版本兼容性。
  2. 依赖传递性
    • Starter 内部通过 Maven/Gradle 的依赖传递机制,自动引入功能所需的所有关联库。
    • 例如:spring-boot-starter-web 会传递引入 Spring MVC、Tomcat、Jackson 等依赖。

工作原理

  1. 依赖聚合与版本管理

    • 父 POM 管理:通过继承 spring-boot-starter-parent,项目自动继承 Spring Boot 统一管理的依赖版本。

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>3.1.0</version>
      </parent>
      
    • BOM(Bill of Materials):若无法继承父 POM,可使用 <dependencyManagement> 引入 spring-boot-dependencies,统一版本管理。

  2. Starter 的内部结构

    • 每个 Starter 仅是一个 空 JAR,其 pom.xml 中声明了相关依赖。

    • 例如:spring-boot-starter-web 的依赖树:

      spring-boot-starter-web
      ├── spring-boot-starter
      ├── spring-boot-starter-json
      ├── spring-boot-starter-tomcat
      ├── spring-webmvc
      └── spring-web
      

五,自动装配

5.1 将自定义类交给IOC容器

假设我们在第三方工具jar包中定义了工具类需要较给Spring的IOC容器管理,因为Spring包扫描默认是启动类所在包下的,所有直接用@Compnent等相关注解是无法将自定义类交给IOC容器管理的。

  • 方案1@ComponentScan({"com.example","com.example2"})组件扫描

    在启动类上添加包扫描即可解决

    @ComponentScan({"com.example","com.example2"})
    @SpringBootApplication
    public class SpringBootWeb{
        
    }
    

    这个方案如果我们有大量类需要较给IOC容器管理要写很多,会很繁琐,不方便阅读

  • 方案2@Import导入。

    使用 @Import导入的类会被Spring加载到IOC容器中,导入形式主要有:

    • 导入普通类

      @Import({TokenParser.class})
      @SpringBootApplication
      public class SpringBootWeb{
          
      }
      

      通过Import导入的普通类不需要加入@Component这种注解可以直接将其交给IOC容器管理

    • 导入配置类

      @Import({HeaderConfig.class})
      @SpringBootApplication
      public class SpringBootWeb{
          
      }
      

      HeaderConfig的内容为

      @Configuration
      public class HeaderConfig{
          
          @Bean
          public HeaderParser headerParser(){
              return new HeaderParser();
          }
          
          @Bean
          public HeaderGenerator headerGenerator(){
              return new HeaderGenerator();
          }
          
      }
      
    • 导入 ImportSelector接口的实现类

      @Import({MyImportSelector.class})
      @SpringBootApplication
      public class SpringBootWeb{
          
      }
      

      MyImportSelector的内容为

      public class MyImportSelector implements ImportSelector{
          public String[] selectImports(AnnotationMetadata importingClassMetadata){
              //返回值封装的是全类名,这里就是将配置类交给IOC容器,然后一次性生成所有配置类里面的bean
              return new String[]{"com.example.HeaderConfig"};
          }
      }
      
  • 方案3:三方依赖提供一个@EnableXxxx注解,里面封装@Import注解

    @EnableHeaderConfig注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Import(MyImportSelector.class)
    //通过import注解引入MyImportSelector
    //通过MyImportSelector引入HeaderConfig
    //通过HeaderConfig引入所有要交给IOC的bean
    public @interface EnableHeaderConfig{
        
    }
    

    这种方案是Spring Boot所采取的方案

5.2 自动装配原理

SpringBoot的自动配置就是当Spring容器启动后,一些配置类、bean对象就自动存入到了lOc容器中,不需要我 们手动去声明,从而简化了开发,省去了繁琐的配置操作。其核心原理可以概括为:基于约定和条件注解,自动扫描并加载所需的 Bean 配置

5.2.1 自动装配的核心机制

  1. @EnableAutoConfiguration 注解

    • @SpringBootApplication 注解组合了 @EnableAutoConfiguration,用于启用自动配置。

    • @EnableAutoConfiguration 通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件中定义的自动配置类

  2. 条件化注解(Conditional Annotations)

    Spring Boot 自动配置类使用条件注解判断是否生效,例如:

    • @ConditionalOnClass:当类路径存在某个类时生效。

    • @ConditionalOnMissingBean:当容器中不存在某个 Bean 时生效。

    • @ConditionalOnProperty:当配置文件中某个属性为特定值时生效。

    • @ConditionalOnWebApplication:当应用是 Web 应用时生效。

  3. 自动配置类的加载流程

    1. 启动类上的 @SpringBootApplication → 触发 @EnableAutoConfiguration

    2. ``SpringFactoriesLoader加载META-INF/spring.factories中所有org.springframework.boot.autoconfigure.EnableAutoConfiguration` 指定的类。

    3. 对每个自动配置类进行条件检查:

      • 检查类路径是否存在所需依赖(如 DataSource.class)。

      • 检查容器中是否已存在相关 Bean。

      • 检查配置文件中的属性设置。

    4. 符合条件的配置类生效,向容器中注册 Bean。


5.2.2 源码追踪

  • @SpringBootApplication注解源码

    //元注解略
    @Inherited
    
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    
    public @interface SpringBootApplication {
        //内容略
    }
    
    • @SpringBootConfiguration注解源码

      //元注解略
      @Configuration//代表声明是一个配置类
      @Indexed//加速应用启动的,不用管
      public @interface SpringBootConfiguration {
          @AliasFor(
              annotation = Configuration.class
          )
          boolean proxyBeanMethods() default true;
      }
      
      

      由此可以得出@SpringBootApplication可以用来声明当前类是一个配置类,所以我们可以直接在启动类中编写三方Bean对象

  • @ComponentScan注解,用于组件扫描的,这个解释了为什么SpringBoot程序默认扫描启动类所在的包

  • @EnableAutoConfiguration注解,自动配置的核心注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    //可以看出导入的是ImportSelector接口的实现类
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }
    
    
    • AutoConfigurationImportSelector实现类源码

      public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
      	//1.这里指定了那些类要交给到IOC中
          public String[] selectImports(AnnotationMetadata annotationMetadata) {
              if (!this.isEnabled(annotationMetadata)) {
                  return NO_IMPORTS;
              } else {
                  //2.本质就是调用getAutoConfigurationEntry()方法来获取要导入的内容
                  AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
                  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
              }
          }
          //3.就是调用这个来获取要导入IOC容器的内容
          protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
              if (!this.isEnabled(annotationMetadata)) {
                  return EMPTY_ENTRY;
              } else {
                  AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                  //5.这个configurations是通过getCandidateConfigurations方法来获得到的
                  List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                  configurations = this.removeDuplicates(configurations);
                  Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                  this.checkExcludedClasses(configurations, exclusions);
                  configurations.removeAll(exclusions);
                  configurations = this.getConfigurationClassFilter().filter(configurations);
                  this.fireAutoConfigurationImportEvents(configurations, exclusions);
                  //4.看上面的getConfigurations可以知道我们需要知道configurations的内容
                  return new AutoConfigurationEntry(configurations, exclusions);
              }
          }
      
          //6.通过这个来获得configurations集合
          protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
              //因为返回的是这个集合,可以看到是通过loadFactoryNames方法来获取的
              List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
              
              ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
              //7.这个是断言,用来进行非空判断,如果是空的就返回提示信息
              //这里可以看到如下俩个文件
              //文件META-INF/spring.factories
              //文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
              //8.可以知道,是通过加载这俩个文件的信息返回到configurations集合当中的
              Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
              return configurations;
          }
          
      }
      

      这个类实现了DeferredImportSelector接口

      public interface DeferredImportSelector extends ImportSelector {
          //内容略
      }
      

      DeferredImportSelector接口继承了ImportSelector

  • 两个文件的位置

    image-20250505170853186

    就是通过这个实现自动配置的,类似的Mybatis也用这种autoconfig的jar包

    spring.factories文件内容,这个是早期用的大概2.7.x之前

    image-20250505171042887

    AutoConfiguration.imports文件内容,这个是Spring新版本用的

    image-20250505171126071

    这些都是要加载的类的全类名,本质上就是读取这俩个文件然后加载到IOC容器之中


5.2.3 条件注解

image-20250505171712761

并不是所有的bean都要加载到IOC容器当中的,是根据条件注解来根据条件来决定那些注解需要加载。

image-20250505172107319

5.3 自定义starter

在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot 的 starter。

命名规范:

  • 对于Spring官方提供的起步依赖命名规范都是:spring-boot-starter-XXX
  • 对于三方主动整合Spring的起步依赖命名规范是:**xxx-**spring-boot-starter

自定义starter需要2个包

  • autoconfigure包用来引入依赖
  • starter包用来管理依赖

需求:自定义aliyun-Oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置。

目标:引入起步依赖引入之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可。

步骤:

  • 创建 aliyun-oss-spring-boot-starter模块

    新建模块

    image-20250505191245099

    修改模块的pom文件

    image-20250505191338837

    删除多余的文件

    image-20250505191322617

  • 创建aliyun-oss-spring-boot-autoconfigure模块,在starter中引入该模块

    新建模块

    image-20250505191421539

    修改模块的pom文件

    image-20250505191450465

    删除多余的文件

    image-20250505191531009

    在starter中引入autoconfig依赖

    image-20250505192126302

  • aliyun-Oss-spring-boot-autoconfigure模块中的定义自动配置功能,并定义自动配置文件META-INF/spring/xxxx.imports

    在starter中引入阿里云OSS相关依赖

    image-20250505192422630

    在starter引入Spring相关依赖

    image-20250505192946596

    编写AliOSSProperties

    @ConfigurationProperties(prefix= "aliyun.oss")
    public class AliOSSProperties{
        private String endpoint;
        private String accessKeyId;
        private String accessKeySecret;
        private String bucketName;
        //get和set方法
        //构造方法
    }
    

    编写AliOSSUtils

    public class AliOSSUtils{
    
        private AliOSSProperties aliOSSProperties;
        
        /**
        实现上传图片到OSS
        */
        public String upload(MultipartFile file) throws IOException{
            //获取阿里云OSS参数
             String endpoint=aliOSSProperties.getEndpoint();
        	 String accessKeyId=aliOSSProperties.getAccessKeyId();
        	 String accessKeySecret=aliOSSProperties.getAccessKeySecret();
        	 String bucketName=aliOSSProperties.getBucketName();
            //获取上传文件的输入流
           	InputStream inputStream=file.getInputStream();
            //避免文件覆盖
            String originalFileName= file.getOriginalFilename();
            String fileName= UUID.randomUUID().toString() + originalFileName.substring(originalFileName.lastIndexOf())
            //上传文件到OSS
            OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);
            ossClient.putObject(bucketName,fileName,inputStream);
            
            //文件访问路径
            String url=endpoint.split("//")[0] + "//"+bucketName+"."+endpoint.split("//")[1]+"/";
            //关闭ossClient
            ossClient.shutdown();
            //把上传到oss的路径返回
            return url;
            
        }
        
    }
    

    编写AliOSSAutoConfiguration

    @Configuration
    @EnableConfigurationProperties(AliOSSProperties.class)
    public class AliOSSAutoConfiguration{
        @Bean
        public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties){
            AliOSSUtils aliOSSUtils= new AliOSSUtils();
            aliOSSUtils.setAliOSSProperties(aliOSSProperties);
            return aliOSSUtils;
        }
        
    }
    

    在resources目录下创建如下目录

    image-20250505193944807

    在文件中写入配置类的全类名

    com.aliyun.oss.AliOSSAutoConfiguration