Spring Boot(一)

时间:2024-03-24 22:00:04

一、springboot框架的特点

  1. 创建独立的Spring应用程序:SpringBoot可以以jar包的形式独立运行,只需通过命令“java–jar xx.jar”即可运行,这使得项目的部署和管理变得非常方便。
  2. 内嵌Servlet容器:SpringBoot使用嵌入式的Servlet容器(例如Tomcat、Jetty或者Undertow等),应用无需打成WAR包,从而进一步简化了Spring应用的搭建和开发过程。
  3. 提供starter简化Maven配置:SpringBoot提供了一系列的“starter”项目对象模型(POMS)来简化Maven配置,这让开发人员可以更加专注于业务逻辑的实现。
  4. 大量的自动配置:SpringBoot提供了大量的默认自动配置,来简化项目的开发,开发人员也可以通过配置文件修改默认配置。
  5. 提供生产就绪型功能:SpringBoot自带应用监控、健康检查、外部配置等生产就绪型功能,这些功能可以帮助开发人员更好地管理和维护应用程序。
  6. 无代码生成和XML配置要求,集成tomcat:SpringBoot不需要任何XML配置即可实现Spring的所有配置,这使得开发过程更加简洁和高效。

二、springboot入门案例

1、新建springboot项目

2、在pom.xml文件中导入web启动依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.qcby</groupId>
    <artifactId>SpringBootDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringBootDemo</name>
    <description>SpringBootDemo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

        <!--加载web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--        文件配置的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 application.yml文件的基本语法

server:
  port: 8085

#yml的配置信息可以直接注入到配置类中

#可存储普通的value
type: yml

#对象
student:
  name: wang
  age: 19
  
#对象的行内写法    --->properties文件无法实现
cat: {color: white, age: 10}

#数组
person:
  - zhang
  - li

#数组的行内写法  
arr: [1,2,3]

application.properties文件的语法

server.port=8085   //配置端口号

通过注解注入

3、创建Person和Dog类

package com.qcby.springbootdemo.dao;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private boolean happy;
    private Date birthday;
    private Map<String,String> maps;
    private List<String> list;
    private Dog dog;

    //必须有set方法才能读取到
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setHappy(boolean happy) {
        this.happy = happy;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", happy=" + happy +
                ", birthday=" + birthday +
                ", maps=" + maps +
                ", list=" + list +
                ", dog=" + dog +
                '}';
    }
}


package com.qcby.springbootdemo.dao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Dog {
    @Value("小白")
    private String name;

    @Value("18")
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4、编写application.yml文件

#依赖注入
person:
  name: qcby
  age: 18
  happy: true
  birthday: 2023/3/3
  maps: {book: java}
  list:
    - java
    - python
    - c
    - c++
  dog:
    name: 小黑
    age: 5

5、测试代码

package com.qcby.springbootdemo;
import com.qcby.springbootdemo.dao.Dog;
import com.qcby.springbootdemo.dao.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootDemoApplicationTests {
    @Autowired
    private Dog dog;

    @Autowired
    private Person person;

    @Test
    void test() {
        System.out.println(dog.toString());
        System.out.println(person.toString());
    }

}

 输出结果:

Dog{name='小白', age=18}

Person{name='qcby', age=18, happy=true, birthday=Fri Mar 03 00:00:00 CST 2023, maps={book=java}, list=[java, python, c, c++], dog=Dog{name='小黑', age=5}}

 三、springboot源码探究

1、父项目(pom文件)

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.4</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

 这个父项目的父项目是

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.7.4</version>
</parent>

他来真正管理SpringBoot应用里面的所有依赖版本;

Spring Boot的版本仲裁中心;

以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)

2、启动器(pom文件)

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

 spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件; Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter,相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器

例如,如果你正在开发一个基于 Web 的应用程序,你可能会使用 spring-boot-starter-web。这个启动器会自动包含如 Spring MVC、Tomcat(或你选择的其他嵌入式 Servlet 容器)、Jackson(用于 JSON 序列化/反序列化)等必要的库。

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

同样地,如果你想要使用数据库,你可以引入 spring-boot-starter-data-jpa 或 spring-boot-starter-jdbc,它们会为你提供与 JPA 或 JDBC 相关的所有依赖项。

3、配置maven插件(pom文件)

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

作用如下:

①自动打包可执行JAR或WAR:

Spring Boot Maven插件会自动收集项目依赖、类文件和其他资源,并将它们打包成一个可执行的JAR或WAR文件。这个JAR或WAR文件是“胖JAR”或“胖WAR”,因为它包含了运行应用程序所需的所有依赖项,这使得部署变得非常简单,因为你只需要一个文件即可。打包后,运用java -jar命令即可执行。

②嵌入服务器支持:

对于Web应用程序,Spring Boot Maven插件支持内嵌的Servlet容器(如Tomcat、Jetty或Undertow),这意味着你不需要在服务器上单独部署你的应用程序。你可以直接通过运行这个JAR或WAR文件来启动你的Web应用程序。

③提供Spring Boot应用程序运行方式:

通过Maven命令(如mvn spring-boot:run),你可以直接在开发环境中启动Spring Boot应用程序,而无需先构建JAR或WAR文件。这对于快速开发和测试非常有用。

④管理依赖版本:

Spring Boot Maven插件与Spring Boot的依赖管理功能协同工作,确保你使用的所有Spring Boot组件版本之间的兼容性。这有助于减少版本冲突问题。

⑤提供其他高级功能:

插件还支持其他高级功能,比如重新打包现有的JAR或WAR文件以包含额外的资源或类,或者创建可执行的程序(Unix/Windows)。

4、主程序类、主入口类

/**
 * @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
 */
@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
        // Spring应用启动起来
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

 ①@SpringBootApplication:Spring Boot的配置类

Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;当你运行 main 方法时,Spring Boot 会自动处理所有的配置细节,包括初始化嵌入式服务器、设置数据源等。

@Target({ElementType.TYPE})   //这个元注解表明 @SpringBootApplication 注解只能应用于类、接口(包括注解类型)或枚举声明。
@Retention(RetentionPolicy.RUNTIME)   //这个元注解表明 @SpringBootApplication 注解在运行时仍然可见,因此可以通过反射读取它。
@Documented    //这个元注解表明当使用 Javadoc 或其他文档工具为类生成文档时,@SpringBootApplication 注解应该被包含在生成的文档中。
@Inherited    //这个元注解表明如果一个类使用了 @SpringBootApplication 注解,那么它的子类也会自动继承这个注解,除非子类明确覆盖了它。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,    //通常用于基于类的类型排除组件。
    classes = {AutoConfigurationExcludeFilter.class}    //用于排除自动配置类,这通常用于避免某些自动配置类被加载。
)}
)
public @interface SpringBootApplication {
    ...
}

@SpringBootApplication 是 Spring Boot 提供的一个核心注解,用于简化 Spring Boot 应用程序的配置。当你使用 @SpringBootApplication 注解时,它实际上是几个其他注解的组合,包括 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan。

以下是这些注解的简要说明:

1) @SpringBootConfiguration:这个注解表明当前类是一个 Spring Boot 配置类,用于定义 beans 和其他配置信息。实际上,它就是一个特殊的 @Configuration 注解,用于加载 Spring Boot 项目的配置。

@Configuration:配置类上来标注这个注解,这个类就被定义为一个配置类

2) @EnableAutoConfiguration:这个注解告诉 Spring Boot 根据你添加的 jar 依赖自动配置你的 Spring 应用程序。例如,如果你在 classpath 中有 spring-boot-starter-web,Spring Boot 就会自动配置一个嵌入式的 Tomcat 服务器和 Spring MVC。

自动配置会尝试根据已定义的 beans、类路径上的内容、属性设置和其他因素来推断你想要的配置。

3) @ComponentScan:这个注解告诉 Spring 在哪个包(及其子包)中查找带有 @Component、@Service、@Repository 和 @Controller 等注解的类,并自动注册这些类为 Spring 应用程序上下文中的 beans。

默认情况下,@SpringBootApplication 注解会扫描启动类所在的包及其子包。

②@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 {};
}
  • @AutoConfigurationPackage:
    • 这个注解用于自动配置包。当 @EnableAutoConfiguration 或 @SpringBootApplication 注解被使用时,它会推断出应该扫描的包,并将该包注册到 Spring Boot 的组件扫描中。这通常基于主应用程序类所在的包。
  • @Import({AutoConfigurationImportSelector.class}):
    • @Import 注解用于导入其他配置类。在这个例子中,它导入了 AutoConfigurationImportSelector 类。AutoConfigurationImportSelector 负责根据项目的依赖和属性设置来决定哪些自动配置类应该被导入到 Spring 应用程序上下文中。
    • @Import(AutoConfigurationPackages.Registrar.class):Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class 将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;

5、Spring Boot 自动配置机制

Spring Boot 在启动时,会扫描类路径下的 META-INF/spring.factories 文件。这些文件通常包含在 Spring Boot 的自动配置依赖中,如 spring-boot-autoconfigure。这些文件中包含了一系列配置键和相应的类名映射。

其中,EnableAutoConfiguration 键对应的值就是一系列自动配置类。当 Spring Boot 应用程序启动时,它会读取这些自动配置类,并将它们作为普通的 Spring 配置类导入到 Spring 应用程序上下文中。

这些自动配置类内部使用 @Configuration、@Bean、@ConditionalOnClass、@ConditionalOnProperty 等注解来定义和注册 beans,并根据类路径下的特定类、属性设置或其他条件来决定是否创建这些 beans。通过这种方式,Spring Boot 能够自动地配置很多常见的应用场景,比如数据源配置、MVC 配置、安全性配置等。

6、J2EE 的整体整合解决方案和自动配置

spring-boot-autoconfigure JAR 文件是 Spring Boot 提供的一个核心模块,它包含了大量的自动配置类。这些自动配置类是针对 J2EE 和其他常用技术栈(如数据库连接池、消息队列、缓存等)的整合解决方案。

例如,如果你的项目包含了某个数据库的驱动 JAR 文件,Spring Boot 会自动配置一个数据源,而无需你手动定义 DataSource bean。同样,如果你的项目中包含了 Spring MVC 的依赖,Spring Boot 会自动配置视图解析器、消息转换器等 MVC 相关的组件。

7、自定义和覆盖自动配置

 虽然自动配置功能非常强大,但 Spring Boot 也提供了灵活的方式来定制和覆盖默认的自动配置行为。

  1. 排除特定的自动配置类:使用 @EnableAutoConfiguration 注解的 exclude 或 excludeName 属性来排除不需要的自动配置类。
  2. 属性配置:通过在 application.properties 或 application.yml 文件中设置特定的属性来覆盖默认的配置。
  3. 提供自己的配置类:你可以定义自己的配置类,并使用 @Configuration 和相关的注解来定义和注册 beans。这些自定义的配置类会覆盖自动配置中定义的相同名称的 beans。

通过这种方式,Spring Boot 既提供了开箱即用的自动配置功能,又允许开发者根据需要进行定制和扩展。

总结:Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东西,自动配置类都帮我们;J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-XXX(版本号).jar

 四、配置文件

1.Springboot的热部署

spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启SpringBoot应用。修改java代码或者配置文件模板后可以刷新页面来实施热部署。引入依赖

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-devtools</artifactId> 
    <optional>true</optional> 
</dependency>

2、配置文件

SpringBoot使用一个全局的配置文件,配置文件名是固定的;

•application.properties

•application.yml

配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;

3、yaml语法

①  基本语法

k:(空格)v:表示一对键值对(空格必须有) 以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的

server:
  port: 8085

属性和值区分大小写

②值的写法

字面量:普通的值(数字,字符串,布尔)

k: v:字面直接来写; 字符串默认不用加上单引号或者双引号; "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: "zhangsan \n lisi":输出;zhangsan 换行 lisi '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据 name: ‘zhangsan \n lisi’:输出;zhangsan\n lisi

③对象、Map(属性和值)(键值对)

k: v:在下一行来写对象的属性和值的关系;注意缩进 对象还是k: v的方式

student:
  name: wang
  age: 19
  
#对象的行内写法    --->properties文件无法实现
cat: {color: white, age: 10}

 ④数组(List、Set)

用- 值表示数组中的一个元素

#数组
person:
  - zhang
  - li

#数组的行内写法  
person: [zhang,li]

 4、配置文件值注入

可以导入配置文件处理器,以后编写配置就有提示了

<!--        导入配置文件处理器,配置文件进行绑定就会有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

 Dog类

@Component
public class Dog {
    private String name;
    
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

①yaml文件写法

person:
  name: qcby
  age: 18
  happy: true
  birthday: 2023/3/3
  pets: cat,dog
  maps: {book: java}
#  list:
#    - java
#    - python
#    - c
#    - c++
  list: [java,c,python,c++]
  dog:
    name: 小黑
    age: 5

②properties文件写法

person.name=qcby  
person.age=18  
person.happy=true  
person.birthday=2023/3/3 
person.pets=cat,dog 
person.maps.book=java  
person.list=java,c,python  
#person.list[0]=java  
#person.list[1]=c  
#person.list[2]=python       
person.dog.name=小黑  
person.dog.age=5

③@Value获取值

不支持复杂类型的注入

八大基本数据类型、String、数组,yaml和properties文件支持注入

list集合、set集合只有properties文件支持注入,类、map集合都不支持

三大引用类型:类、接口、数组

@Component   //交给spring管理
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private Integer age;
    @Value("${person.happy}")
    private boolean happy;
    @Value("${person.birthday}")
    private Date birthday;
    //@Value("${person.pets}")
    private String[] pets;
    //@Value("${person.maps}")   使用@Value不能直接读取
    private Map<String,String> maps;
    //@Value("${person.list}")   //只有properties文件中person.list=java,c,python  才能读取到
    @Value("#{'${person.list}'.split(',')}")
    //@Value("#{'${person.list:}'.split(',')}")  加上冒号后,当不配置该 key 值,默认值会为空串,它的 length = 1(不同于数组,length = 0),这样解析出来 list 的元素个数就不是空了。
    private List<String> list;
    //@Value("${person.dog}")   使用@Value不能直接读取,但是在Dog类中使用@Value("${person.dog.name}")和@Value("${person.dog.age}")可以注入
    private Dog dog;

    //必须有set方法才能读取到
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setHappy(boolean happy) {
        this.happy = happy;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    public void setPets(String[] pets) {
        this.pets = pets;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

   @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", happy=" + happy +
                ", birthday=" + birthday +
                ", pets=" + Arrays.toString(pets) +
                ", maps=" + maps +
                ", list=" + list +
                ", dog=" + dog +
                '}';
    }
}

④@ConfigurationProperties获取值

所有类型注入都支持

/**
 * 将配置文件中的每一个属性的值,映射到这个组件中
 * @ConfigurationProperties:告诉springboot将本类中的所有属性和配置文件中的相关配置进行绑定
 * prefix = "person":配置文件中哪个下面的所有属性进行一一映射
 */
@Component   //交给spring管理
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private boolean happy;
    private String[] pets;
    private Date birthday;
    private Map<String,String> maps;
    private List<String> list;
    private Dog dog;

    //必须有set方法才能读取到
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setHappy(boolean happy) {
        this.happy = happy;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    public void setPets(String[] pets) {
        this.pets = pets;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", happy=" + happy +
                ", birthday=" + birthday +
                ", pets=" + Arrays.toString(pets) +
                ", maps=" + maps +
                ", list=" + list +
                ", dog=" + dog +
                '}';
    }
}

⑤@Value 和 @ConfigurationProperties 的比较

https://www.cnblogs.com/mjtabu/p/14297053.html

@Value

优点:

  1. 简单明了:用于注入单个属性值,代码简洁直观。
  2. 灵活性高:可以注入各种类型的数据,包括基本类型、字符串、枚举等。
  3. 支持SpEL:可以在@Value注解中使用Spring Expression Language(SpEL)来执行复杂的表达式。

缺点:

  1. 不支持复杂属性:对于像列表、集合、映射这样的复杂类型,@Value无法直接处理。
  2. 配置错误难以发现:如果属性名拼写错误或配置文件中不存在该属性,将抛出异常,但异常信息可能不够直观,难以定位问题。

@ConfigurationProperties

优点:

  1. 支持复杂属性:能够轻松绑定复杂类型的属性,如列表、集合、映射等。
  2. 校验功能:结合@Validated注解,可以对配置的属性进行校验,确保数据的合法性。例如@Email用于校验字段必须是邮箱格式
  3. 易于管理:将多个相关配置聚合到一个类中,便于统一管理和维护。

缺点:

  1. 相对复杂:需要创建一个配置类来绑定属性,相对于@Value来说稍显繁琐。
  2. 性能考虑:由于@ConfigurationProperties需要进行类型转换和校验,可能会稍微影响性能,特别是在大量使用的情况下。

使用场景

  • 使用@Value的场景:
    • 当需要注入单个简单的属性值时。
    • 当需要在运行时根据条件动态计算属性值时(利用SpEL)。
  • 使用@ConfigurationProperties的场景:
    • 当需要批量注入多个相关配置时。
    • 当配置的属性类型较复杂,如列表、集合、映射等。
    • 需要对配置的属性进行校验时。

⑥@PropertySource:加载指定的配置文件

例如,自定义person2.propertise文件

#server.port=8081
#server.servlet.path=/tx

person2.name=阳哥${random.uuid}
person2.age=17
person2.happy=false
person2.birthday=2017/2/5
person.pets=dog,cat,animal
person2.list=python,java,c
person2.maps.key1=value1
person2.maps.key2=value2
person2.dog.name=${person.hello:hello}_dog    
#如果存在person.hello,取其值连接_dog,否则是hello_dog
person2.dog.age=12

 加载配置文件

@PropertySource(value = {"classpath:person2.properties"})
@ConfigurationProperties(prefix = "person2")
@Component   //交给spring管理
public class Person {
    ...
    }

随机数

${random.value}、${random.int}、${random.long} ${random.int(10)}、${random.int[1024,65536]}

占位符获取之前配置的值,如果没有可以是用:指定默认值

person2.dog.name=${person.hello:hello}_dog

#如果存在person.hello,取其值连接_dog,否则是hello_dog 

5、Profile

我们在主配置文件编写的时候,文件名可以是

application-{profile}.properties/yml 默认使用application.properties的配置;

①yml支持多文档块方式

server:
  port: 8085  #没有特定profile激活时的默认设置
spring:
  profiles:
    active: dev

---
server:
  port: 8081
spring:
  profiles: dev   #指定属于哪个文件

---
server:
  port: 8082
spring:
  profiles: prod

 在properties文件中指定激活配置文件

spring.profiles.active=dev

命令行:

  java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev; 可以直接在测试的时候,配置传入命令行参数

虚拟机参数; -Dspring.profiles.active=dev

②配置文件加载位置

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

–file:./config/ (与src同级)

–file:./

–classpath:/config/ (resources)

–classpath:/

优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件;互补配置;

在-classpath:/的application.properties.中加入server.servlet.context-path=/boot3访问根目录

6、外部配置加载顺序

SpringBoot也可以从以下位置加载配置;优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置

1)命令行参数

所有的配置都可以在命令行上进行指定java -jar xxx.jar--server.port=8087 --server.context-path=/abc

java -jar xxx.jar --spring.config.location=C:/appliction.properties多个配置用空格分开; --配置项=值

2) 来自java:comp/env的JNDI属性

3) Java系统属性(System.getProperties())

4) 操作系统环境变量

5) RandomValuePropertySource配置的random.*属性值 由jar包外向jar包内进行寻找;优先加载带profile

6) jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

7) jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件 再来加载不带profile

8) jar包外部的application.properties或application.yml(不带spring.profile)配置文件

9) jar包内部的application.properties或application.yml(不带spring.profile)配置文件

10) @Configuration注解类上的@PropertySource 

11) 通过SpringApplication.setDefaultProperties指定的默认属性

7、spring boot自动配置原理

  1. 启动类加载与自动配置启动:当Spring Boot应用启动时,主配置类(通常是带有@SpringBootApplication注解的类)会被加载。@SpringBootApplication注解是一个复合注解,其中包含了@EnableAutoConfiguration。这个注解告诉Spring Boot开启自动配置功能。
  2. 自动配置导入器:@EnableAutoConfiguration注解使用AutoConfigurationImportSelector来导入自动配置类。这个选择器会查找类路径下META-INF/spring.factories文件中定义的自动配置类。这些类通常遵循*AutoConfiguration的命名约定。
  3. 候选配置收集:AutoConfigurationImportSelector通过getCandidateConfigurations方法收集所有候选的自动配置类。这些类基于spring.factories文件中的条目确定。
  4. 自动配置类的加载与注册:收集到的每个自动配置类都会被Spring容器加载和注册为bean。这些自动配置类可能包含@Bean方法,用于向容器中注册组件,也可能包含@Configuration注解,以定义更多的bean。
  5. 条件化配置:自动配置类通常使用条件注解(如@ConditionalOnClass、@ConditionalOnProperty、@ConditionalOnMissingBean等)来确定它们是否应该被应用。这些条件注解允许自动配置类基于类路径上是否存在特定的类、配置文件中是否存在特定的属性、或者容器中是否存在特定的bean等条件来决定是否生效。
  6. 属性绑定:自动配置类通常依赖属性类(通常遵循*Properties的命名约定)来提供配置属性。这些属性类中的属性与配置文件(如application.properties或application.yml)中的属性绑定。Spring Boot通过@ConfigurationProperties注解和PropertySource注解等机制实现这种绑定。
  7. 配置类的生效与组件添加:当自动配置类满足所有条件并生效时,它会向Spring容器中添加必要的组件(bean)。这些组件可以是Spring框架的组件,也可以是第三方库的组件,它们共同构成了应用的基础设施。

以HttpEncodingAutoConfiguration为例,这个自动配置类根据条件判断(如是否启用了HTTP编码设置)来决定是否生效。如果生效,它会向容器中添加一个CharacterEncodingFilter bean,用于设置HTTP请求的字符编码。这个自动配置类还依赖HttpEncodingProperties类来获取配置文件中定义的属性(如字符编码的默认值)

@Configuration 
//表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件@EnableConfigurationProperties(HttpEncodingProperties.class) 
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication 
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class) 
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing=true) 
//判断配置文件中是否存在某个配置spring.http.encoding.enabled;如果不存在,判断也是成立的;即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration {      
//他已经和SpringBoot的配置文件映射了      
    private final HttpEncodingPropertiesproperties;      
    //只有一个有参构造器的情况下,参数的值就会从容器中拿      
    public HttpEncodingAutoConfiguration(HttpEncodingPropertiesproperties)                    {
        this.properties = properties;      
    } 
    @Bean 
    //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @ConditionalOnMissingBean(CharacterEncodingFilter.class) 
    //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter()
    {
       CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
       filter.setEncoding(this.properties.getCharset().name());
       filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
       filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
       return filter;
}
    
    
    
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值和bean的属性进行绑定
public class HttpEncodingProperties {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");

总结:

1)SpringBoot启动会加载大量的自动配置类

2)我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;

3)我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)

4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

xxxxAutoConfigurartion:自动配置类;给容器中添加组件

8、@Conditional

@Conditional 是 Spring Boot 中用于条件化配置的核心注解。它允许开发者基于满足特定条件来创建 bean,这些条件可能涉及类路径中的类是否存在、配置文件中是否有特定的属性设置、特定的 bean 是否已经在 Spring 容器中注册等。@Conditional 注解本身是一个元注解,通常不直接使用,而是使用其派生注解或者自定义条件。

Spring Boot 提供了许多基于 @Conditional 的派生注解,这些注解可以更方便地表达不同的条件。以下是一些常见的派生注解:

​​​​​​​

这些派生注解极大地简化了条件化配置的过程,使得开发者能够更加灵活地控制哪些配置类应该被加载和哪些 bean 应该被创建。

怎么知道哪些自动配置类生效

  1. @ConditionalOnClass:当类路径上存在指定的类时,条件成立。
  2. @ConditionalOnMissingClass:当类路径上不存在指定的类时,条件成立。
  3. @ConditionalOnBean:当容器中存在指定的 bean 时,条件成立。
  4. @ConditionalOnMissingBean:当容器中不存在指定的 bean 时,条件成立。
  5. @ConditionalOnProperty:当指定的配置属性存在并且满足特定条件时(如值为 true),条件成立。
  6. @ConditionalOnResource:当类路径上存在指定的资源时,条件成立。
  7. @ConditionalOnExpression:当 SpEL(Spring Expression Language)表达式的结果为 true 时,条件成立。
  8. @ConditionalOnJava:当 Java 版本符合特定条件时,条件成立。
  9. @ConditionalOnJndi:当 JNDI 存在时,条件成立。
  10. @ConditionalOnOs:当操作系统满足特定条件时,条件成立。
  11. @ConditionalOnWebApplication:当应用是 Web 应用时,条件成立。
  12. @ConditionalOnNotWebApplication:当应用不是 Web 应用时,条件成立。
# application.properties  
debug=true