SpringBoot 第二章 配置

1、配置文件

SpringBoot使用一个全局的配置文件,配置文件名是固定的;
• application.properties
• application.yml

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

YAML(YAML Ain't Markup Language)

  • YAML A Markup Language:是一个标记语言
  • YAML isn't Markup Language:不是一个标记语言;

标记语言:

  • 以前的配置文件;大多都使用的是 xxxx.xml文件;
  • ​ YAML:以数据为中心,比json、xml等更适合做配置文件;
  • YAML:配置例子
server:
  port: 8081

​ XML:

<server>
    <port>8081</port>
</server>

2、YAML语法

k:(空格)v:表示一对键值对(空格必须有);

  • 空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
server:
    port: 8081
    path: /hello
  • 属性和值也是大小写敏感;

值的写法

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

  • k: v:字面直接来写;

字符串默认不用加上单引号或者双引号;

  • "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 如果转移的话会 \n

  • ​name: "zhangsan \n lisi":输出;zhangsan 换行 lisi

  • '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

  • name: 'zhangsan \n lisi':输出;zhangsan \n lisi

对象、Map(属性和值)(键值对):
k: v:在下一行来写对象的属性和值的关系;注意缩进

  • 对象还是k: v的方式
    friends:
    lastName: zhangsan
    age: 20
  • 行内写法:
    friends: {lastName: zhangsan,age: 18}

数组(List、Set):

  • 用- 值表示数组中的一个元素
    pets:
    - cat
    - dog
    - pig
  • 行内写法
    pets: [cat,dog,pig]

3、需要导入对应依赖包

<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
测试包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
  • 注意一:在每一个属性需要添加空格 属性名:空格 属性值
  • 注意二:需要增加get set 方法来设置值
  • 注意三:isStudent => getStudent =x=> yaml中isStudent匹配不上
  • 注意四:需要加入到容器中
    /**
    * 将配置文件中配置的每一个属性的值,映射到这个组件中
    * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
    *      prefix = "person":配置文件中哪个下面的所有属性进行一一映射
    * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
    */
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
      private String nam;
      private Integer age;
      private Boolean student;
      private ArrayList<String> book;
    略get set方法 + 略toString()
    }
    测试类
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class MyTest {
      @Autowired
      private Person person;
      @Test
      public void myTestFunc(){
          System.out.println(person);
      }
    }
    Person{name='cznczai', age=18, student=false, book=[java编程思想, 算法导论, 算法]}
    application.yaml文件
    person:
    name: cznczai
    age: 18
    Student: true
    book: {java编程思想,算法导论,算法}
    application.properties文件 其他代码跟yaml一致
    person.name=cznczai
    person.age=18
    person.book=java,c,python
    person.student=true
    Person{name='cznczai', age=18, student=true, book=[java, c, python]}

4、@Value

获取值和@ConfigurationProperties获取值比较
普通的配置文件

<bean class="Person">
    <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
</bean>

图片说明

@value的代码

@Component
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("#{20+2}")
    private Integer age;
    @Value("true")
    private Boolean student;
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", student=" + student + '}';
    }
}
//Person{name='cznczai', age=22, student=true}
@Component
@Validated
public class Person {
    @Value("${person.name}")
    @Email
    private String name;
}
校验没出意外 不支持Validate校验

@ConfigurationProperties情况

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean student;
//必要的set方法
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setStudent(Boolean student) {
        this.student = student;
    }
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", student=" + student + '}';
    }
}
//不是一个合法的电子邮件地址
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
    @Email
    private String name;
}
//person.age=#{18*2}
出错 报异常

控制层获取取值

@RestController
public class MyController {
    @Value("${name}")
    private String name;
    @RequestMapping("hello")
    public String hello(){
        return "hello,"+name;
    }
}

图片说明

配置文件yml还是properties他们都能获取到值;

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;

如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;

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

@PropertySource(value="classpath:myperson.properties")
@Component
@ConfigurationProperties(prefix = "my") //与PropertySource搭配使用 设置前缀
//注释后 即使是没有前缀 也是没有值的 也就是失效了 不可以注释
public class Person {
    private String name;
    private Integer age;
    private Boolean student;

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

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

    public void setStudent(Boolean student) {
        this.student = student;
    }
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", student=" + student + '}';
    }
}
person.name=cznczai
person.age=18
person.student=true

my.name=wcl
my.age=20
my.student=true

5、@ImportResource

导入Spring的配置文件,让配置文件里面的内容生效;spring.xml

Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;

想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类

@SpringBootApplication
@ImportResource(locations = "classpath:spring.xml") //需要放在这里 但是没有执行main方法
public class MySpringBootApplication {
    public static void main(String[] args) {
        System.out.println("Main~~");
        SpringApplication.run(MySpringBootApplication.class,args);
    }
}
    <bean id="person" class="com.project.bean.Person"></bean>
  @Test
    public void myTestFunc(){
        System.out.println(ioc.containsBean("person"));//true
    }

6、springboot推荐的方法

SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式

/**
 * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
 * 在配置文件中用<bean><bean/>标签添加组件
 */
@Configuration
//第一种方式
//@ImportResource(locations = "classpath:spring.xml")
public class MyConfig {
    @Bean //第二种方式
    //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
    public Person person(){
        return new Person();
    }
}

7、占位符配置

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

  • RadomValuePropertySource:配置文件中可以使用随机数
    ${random.value} ${random.int} ${random.long}
    ${random.int(10)} ${random.int[1024,65536]}
  • 属性配置占位符
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
    }
    person.name=cznczai-${random.uuid}
    person.age=${random.int}
    person.student=${person.hello:true}  :后面是默认值
    Person{name='cznczai-8fc4ca51-eafd-490c-96e5-741474cac04b', age=2003572828, student=true}

8、多Profile文件

Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境

  • 多profile文件形式:
    -格式:application-{profile}.properties:dev/prod
  • 多profile文档块模式
  • 激活方式
    -命令行--spring.profiles.active=dev
    -配置文件 spring.profiles.active=dev
    -jvm参数-Dspring.profiles.active=dev

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

application-{profile}.properties/yml

默认使用application.properties的配置;
修改配置

------>application.yml<------
spring:
  profiles:
    active: dev
server:
  port: 8081
------>application-dev.yml<--------
server:
  port: 8085

同个配置文件 多个运行环境

server:
  port: 8081
spring:
  profiles:
    active: dev
---
server:
  port: 8083
spring:
  profiles: dev
---
server:
  port: 8085
spring:
  profiles: prod

在配置文件中指定

  • 通过命令行配置
    图片说明
  • 配置第二步
    图片说明
  • 结果
    即使在配置文件中已经配置了prod环境
    图片说明

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

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

8、配置文件加载位置

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

  • –file:./config/ 最高
  • –file:./
  • –classpath:/config/
  • –classpath:/ 最低

优先级由高到底,高优先级的配置会覆盖低优先级的配置;同种配置的话 先加载的最高还是会覆盖掉最低优先级的

SpringBoot会从这四个位置全部加载主配置文件;互补配置

config/application.yml 优先级√

server:
  port: 8087

application.yml

server:
  port: 8081
server.context.path=/boot   <- 互补配置 不是说看最高优先级的配置文件就不看了

图片说明

我们还可以通过spring.config.location来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;

9、外部配置加载顺序

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

修改默认配置

  1. 命令行参数

    所有的配置都可以在命令行上进行指定
    java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
    多个配置用空格分开; --配置项=值

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

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

  4. 操作系统环境变量

  5. RandomValuePropertySource配置的random.*属性值

由jar包外向jar包内进行寻找;

优先加载带profile

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

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

再来加载不带profile

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

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

  3. @Configuration注解类上的@PropertySource

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

直接在同个文件夹增加信息的配置文件
图片说明
启动就会加载外面的路径
图片说明

10、自动配置原理

1)、SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
2)、@EnableAutoConfiguration 作用:

  • 利用AutoConfigurationImportSelector给容器中导入一些组件?【原先是有EnableAutoConfigurationImportSelector的类 但是归并到AutoConfigurationImportSelector】
    利用selectImports() 来导入组件
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
                autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
       AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
    } || 旧代码编程了现在的代码
      \/
    protected AutoConfigurationEntry getAutoConfigurationEntry(
       List<String> configurations = getCandidateConfigurations(annotationMetadata,    attributes);
    }  ||
       \/
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) { 
       List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    }
    扫描所有jar包类路径下  META-INF/spring.factories
    把扫描到的这些文件的内容包装成properties对象
    从properties中获取到getSpringFactoriesLoaderFactoryClass()==>EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
       ||
       \/
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    } 
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        //url包装成properties
        URL url = (URL)urls.nextElement();
        UrlResource resource = new UrlResource(url);
        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    }
    抽取一部分
    将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;
    图片说明
    每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;

3)、每一个自动配置类进行自动配置功能;
||
\/
4)、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;

  • 代码

    @Configuration  //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
    @EnableConfigurationProperties(HttpProperties.class)
    //启动指定类的ConfigurationProperties功能;并把HttpProperties加入到ioc容器中【之前的HttpEncodingProperties再新版本没有了 变成了静态内部类public static class Encoding {}】
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;    
    //判断当前应用是否是web应用,如果是,当前配置类生效
    
    @ConditionalOnClass(CharacterEncodingFilter.class)
    //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
    
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)  
    //判断配置文件中是否存在某个配置  spring.http.encoding.enabled;如果不存在,判断也是成立的
    //即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
    public class HttpEncodingAutoConfiguration {
        //他已经和SpringBoot的配置文件映射了
      private final HttpProperties.Encoding properties;
          //只有一个有参构造器的情况下,参数的值就会从容器中拿
          public HttpEncodingAutoConfiguration(HttpProperties properties) {
          this.properties = properties.getEncoding();
      }
      @Bean    //给容器中添加一个组件,这个组件的某些值需要从properties中获取//@EnableConfigurationProperties的作用
      @ConditionalOnMissingBean//没有就配置
      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;
      }
    }

    根据当前不同的条件判断,决定这个配置类是否生效?

    一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;

5)、所有在配置文件中能配置的属性都是在xxxxProperties类中封装者‘;配置文件能配置什么就可以参照某个功能对应的这个属性类

  • 代码
    @ConfigurationProperties(prefix = "spring.http")//从配置文件中获取指定的值和bean的属性进行绑定
    public class HttpProperties {
      public static class Encoding {
              public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
      }
      字段 也是配置文件配置目标
      字段
    }

精髓:

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

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

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

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

xxxxAutoConfigurartion:自动配置类; 需要♥

给容器中添加组件

xxxxProperties:封装配置文件中相关属性; 目标♥

@Conditional

  • @Conditional派生注解(Spring注解版原生的@Conditional作用)
    作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

    @Conditional扩展注解 作用(判断是否满足当前指定条件)
    @ConditionalOnJava 系统的java版本是否符合要求
    @ConditionalOnBean 容器中存在指定Bean;
    @ConditionalOnMissingBean 容器中不存在指定Bean;
    @ConditionalOnExpression 满足SpEL表达式指定
    @ConditionalOnClass 系统中有指定的类
    @ConditionalOnMissingClass 系统中没有指定的类
    @ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
    @ConditionalOnProperty 系统中指定的属性是否有指定的值
    @ConditionalOnResource 类路径下是否存在指定资源文件
    @ConditionalOnWebApplication 当前是web环境
    @ConditionalOnNotWebApplication 当前不是web环境
    @ConditionalOnJndi JNDI存在指定项

    自动配置类必须在一定的条件下才能生效;

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

    我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

    Positive matches: (自动配置类启用的)
     CodecsAutoConfiguration matched:
        - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)
    ....
    Negative matches: (没有启动,没有匹配成功的自动配置类)
     ActiveMQAutoConfiguration:
        Did not match:
           - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
    ...

springboot自动配置 idea
先ctrl alt shift n 找 *autoconfiguration

全部评论

相关推荐

不愿透露姓名的神秘牛友
10-05 10:13
已编辑
HHHHaos:让这些老登来现在秋招一下,简历都过不去
点赞 评论 收藏
分享
面试摇了我吧:啊哈哈面试提前五个小时发,点击不能参加就是放弃
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务