SpringBoot学习笔记

1、简介

1.1、什么是Spring

Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架。

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发

1.2、Spring是如何简化Java开发的

为了降低java开发的复杂性,Spring采用了以下4种关键策略:

  1. 基于POJO的轻量级和最小入侵式编程,所有的东西都是Bean
  2. 通过控制反转(IOC),依赖注入(DI)和面向切面编程(AOP)实现松耦合
  3. 基于切面(AOP)和惯例进行声明式编程;
  4. 通过切面和模板减少样式代码,Redis Template

1.3、什么是Spring Boot

Spring Boot就是一个JavaWeb的开发框架简化开发,约定大于配置

Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多的设置,多数Spring Boot应用只需要很少的Spring配置。

Spring Boot 同时集成了大量常用的第三方库配置(Redis,MongoDB,Jpa等)。

Spring Boot 其实并不是新的框架,它默认配置了很多框架的使用方式,就像Maven整合了所有的jar包,Spring Boot只是整合了所有的框架。

Spring Boot的优点:

  • 使所有Spring开发者更快的入门
  • 开箱即用,提供了各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求

2、Hello World

// 相当于@Controller + @ResponseBody
@RestController
public class  HelloController {

    @RequestMapping("/hello")
    public String hello(){
        return "Hello World";
    }
}

3、原理初探

3.1、pom.xml

3.1.1、父依赖

pom.xml主要是依赖一个父项目,主要负责管理项目的资源过滤和插件

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

点进去,发现还有一个父依赖

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

这里是管理Spring Boot应用里面所有依赖版本的地方。

以后导入依赖默认是不需要写版本的,除非要导入的包不在被管理的依赖中,这时候就需要手动配置版本,

3.1.2、启动器spring-boot-starter

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

spring-boot-starter-xxx:就是spring-boot的场景启动器。

spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件。

3.2、主启动类

3.2.1、默认的主启动类

// @SpringBootApplication 来标注一个主程序类
// 说明这是一个Spring Boot应用
@SpringBootApplication
public class HelloSpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloSpringBootApplication.class, args);
    }

}

3.2.2、@SpringBootApplication

作用:标注在某个类上,说明这个类是SpringBoot的主配置类SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用

进入这个注解,会发现上面还有很多其他注解。

@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}
)}
)
  • 四个元注解:@Retention @Target @Document @Inherited;
java中元注解有四个: @Retention @Target @Document @Inherited;

  @Retention:注解的保留位置         

      @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含

      @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

      @Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

  @Target:注解的作用目标

        @Target(ElementType.TYPE)   //接口、类、枚举

        @Target(ElementType.FIELD) //字段、枚举的常量

        @Target(ElementType.METHOD) //方法

        @Target(ElementType.PARAMETER) //方法参数

        @Target(ElementType.CONSTRUCTOR)  //构造函数

        @Target(ElementType.LOCAL_VARIABLE)//局部变量

        @Target(ElementType.ANNOTATION_TYPE)//注解

        @Target(ElementType.PACKAGE) ///包   

    @Document:// 说明该注解将被包含在javadoc中

   @Inherited:// 说明子类可以继承父类中的该注解

  • @ComponentScan

    这个注解在Spring中很重要,它对应XML配置中的元素。

    作用:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中。

  • @SpringBootConfiguration

    作用:SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类。

    进去这个注解看

    @Configuration
    public @interface SpringBootConfiguration{}
    
    // 点进@Configuration
    @Component
    public @interface Configuration {}
    

    这里的@Configuration,说明这是一个配置类,配置类就是对应Spring的xml配置文件;

    里面的@Component说明,启动类本身也是Spring中的一个组件,负责启动应用。

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

    点进这个注解:

    • @AutoConfigurationPackage:自动配置包
    @Import({Registrar.class})public @interface AutoConfigurationPackage {}
    
    • @import:Spring底层注解,给容器中导入一个组件。

      Registrar.class作用:将主启动类的所在包及包下面所有子包里面的所有组件(@Component、@Service、@Controller、@Repository)扫描导入到Spring容器。

    • @Import({AutoConfigurationImportSelector.class}):给容器导入组件

      AutoConfigurationImportSelector.class:自动配置导入选择器,那么它会导入哪些组件的选择器呢?通过源码来看一下。

      1. 进入这个类,发现有getCandidateConfigurations方法
      protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");    return configurations;}
      
      1. 这个方法又调用了SpringFactoriesLoader类的静态方法loadFactoryNames,进入这个方法
      public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {    ClassLoader classLoaderToUse = classLoader;    if (classLoader == null) {        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();    }    String factoryTypeName = factoryType.getName();    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}
      
      1. 继续查看loadSpringFactories方法
      private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {    Map<String, List<String>> result = (Map)cache.get(classLoader);    if (result != null) {        return result;    } else {        HashMap result = new HashMap();        try {            Enumeration urls = classLoader.getResources("META-INF/spring.factories");            while(urls.hasMoreElements()) {                URL url = (URL)urls.nextElement();                UrlResource resource = new UrlResource(url);                Properties properties = PropertiesLoaderUtils.loadProperties(resource);                Iterator var6 = properties.entrySet().iterator();                while(var6.hasNext()) {                    Entry<?, ?> entry = (Entry)var6.next();                    String factoryTypeName = ((String)entry.getKey()).trim();                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());                    String[] var10 = factoryImplementationNames;                    int var11 = factoryImplementationNames.length;                    for(int var12 = 0; var12 < var11; ++var12) {                        String factoryImplementationName = var10[var12];                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {                            return new ArrayList();                        })).add(factoryImplementationName.trim());                    }                }            }            result.replaceAll((factoryType, implementations) -> {                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));            });            cache.put(classLoader, result);            return result;        } catch (IOException var14) {            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);        }    }}
      
      1. 发现一个资源文件spring.factories
      2. 然后发现这个文件在,spring-boot-autoconfigure的jar包下

4、yaml

4.1、配置文件

Spring Boot 使用一个全局的配置文件,配置文件名称是固定的

  • application.properties
    • 语法结构:key=value
  • application.yaml
    • 语法结构:key: 空格 value

配置文件的作用:修改SpringBoot自动配置的默认值。因为Spring Boot在底层都已经自动配置好了。

4.2、yaml概述

YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)

这种语言以数据为中心,而不是以标记语言为重点

yaml 和 xml 对比:

  • xml配置:

    <server>    <port>8081</port></server>
    
  • yaml配置

    server:  port: 8081
    

4.3、yaml基础语法

  • 属性和值的大小写都是敏感的;
  • 使用缩进表示层级关系,只要是左边对齐的一列数据都是同一层级的;
  • 冒号后的空格不能省略;
  • # 表示注释。

yaml 支持三种数据结构:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes)/ 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence)/列表(list)
  • 纯量(scalars):单个的、不可再分的值(整型、时间、日期、null、字符串、浮点型、布尔型)

1、对象

对象的一组键值对,使用冒号结构表示(冒号后有空格)

port: 8001

另一种写法,将所有的键值对写成一个行内对象。

user: {name: gmt,age: 12}

2、数组

一组连词线开头的行,构成一个数组

- name- age- id

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。

gmt:   - age  - id  - phone

数组也可以采用行内表示法

gmt: [ name,age,phone ]

3、复合结构

对象和数组可以结合使用,形成复合结构

gmt:  - age: 12  - id: 9527  - phone: 10086

4、纯量

纯量是最基本的、不可再分的值。

  • 布尔值:truefalse

  • null:用~表示

  • 整数:例如12

  • 浮点数:例如12.12

  • 时间:采用ISO8601格式。例如iso8601: 2001-12-14t21:59:43.10-05:00

  • 日期:采用复合ISO8601格式的年月日表示。例如:date: 1976-07-31

  • 字符串:是最常见也是最复杂的一种数据类型

    • 字符串默认不使用引号表示:

      str: 这是一行字符串
      
    • 如果字符串中包含空格或者特殊字符,需要放在引号中

      str: '内容: 字符串'
      

      单引号和双引号都可以使用,双引号不会对特殊字符转义:

      str1: '内容\n字符串'str2: "内容\n字符串"
      

      转为JavaScript如下:

      { str1: '内容\\n字符串', str2: '内容\n字符串' }
      
    • 字符串可以写成多行,从第二行开始必须有一个单空格缩进。换行符会被转为空格。

      str: 这是一段  多行  字符串
      

      转为JavaScript如下:

      { str: '这是一段 多行 字符串' }
      
    • 多行字符串可以使用|保留换行符,也可以是使用>折叠换行

      this: |  Foo  Barthat: >  Foo  Bar
      

      转为JavaScript如下:

      { this: 'Foo\nBar\n', that: 'Foo Bar\n' }
      
    • +表示保留字符串末尾的换行,-表示删除字符串末尾的换行

      s1: |  Foo s2: |+  Foo  s3: |-  Foo
      

      转为JavaScript如下:

      { s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' }
      
    • 字符串之中可以插入HTML标记

      message: |   <p style="color: red">    段落  </p>
      

      转为JavaScript如下:

      { message: '\n<p style="color: red">\n  段落\n</p>\n' }
      

yaml 允许使用两个感叹号,强制转换数据类型

e: !!str 123

转化为JavaScript如下:

{ e: '123' }

5、引用

锚点& 和别名*,可以用来引用。

defaults: &defaults  adapter:  postgres  host:     localhost development:  database: myapp_development  <<: *defaults test:  database: myapp_test  <<: *defaults

等同于如下代码

defaults:  adapter:  postgres  host:     localhost development:  database: myapp_development  adapter:  postgres  host:     localhost test:  database: myapp_test  adapter:  postgres  host:     localhost

& 用来建立锚点,<<表示合并到当前数据,*用来引用锚点

4.4、注入配置文件

yaml文件更强大的地方在于,它可以给我们的实体类直接注入匹配值。

1、注入配置文件

  1. 在SpringBoot项目中的resources目录下,新建一个application.yaml

  2. 编写一个实体类 Dog

    package com.gmt.hellospringboot.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Component // 注册Bean@Data@NoArgsConstructor@AllArgsConstructorpublic class Dog {    private String name;    private Integer age;}
    
  3. 我们原来是如何给bean注入属性值的。@Value

    @Component@Data@NoArgsConstructor@AllArgsConstructorpublic class Dog {    @Value("大黄")    private String name;    @Value("12")    private Integer age;}
    
  4. 在SpringBoot的测试类下注入Dog输出一下

    @SpringBootTestclass HelloSpringBootApplicationTests {    @Autowired    Dog dog;    @Test    void contextLoads() {        System.out.println(dog);    }}
    

    输出:

    Dog(name=大黄, age=12)

  5. 编写Person类

    @Component@Data@AllArgsConstructor@NoArgsConstructorpublic class Person {    private String name;    private Integer age;    private Dog dog;    private List<String> hobby;    private Map<String,Object> maps;    private Date birth;}
    
  6. 使用yaml配置的方式注入,编写application.yaml

    名称要和Person类的变量名一致,不然会输出null

    person:  name: gmt  age: 12  dog:    name: 大黄    age: 11  hobby:    - 唱    - 跳    - rap    - 篮球  maps: {k1: v1, k2: v2}  birth: 2000/01/01
    
  7. 将值注入到Person类中

    @Component@Data@AllArgsConstructor@NoArgsConstructor@ConfigurationProperties(prefix = "person")/*@ConfigurationProperties作用:将配置文件中配置的每一个属性的值,映射到这个组件中;告诉SpringBoot将奔雷中的所有属性和配置文件中相关的配置进行绑定参数prefix = "person" :将配置文件中的person下的所有属性一一对应*/public class Person {    private String name;    private Integer age;    private Dog dog;    private List<String> hobby;    private Map<String,Object> maps;    private Date birth;}
    
  8. IDEA 提示,springboot配置注解处理器没有找到,导入依赖

    <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-configuration-processor</artifactId>    <optional>true</optional></dependency>
    
  9. 测试

    @SpringBootTestclass HelloSpringBootApplicationTests {    @Autowired    Person person;    @Test    void contextLoads() {        System.out.println(person);    }}
    

    输出:

    Person(name=gmt, age=12, dog=Dog(name=大黄, age=11), hobby=[唱, 跳, rap, 篮球], maps={k1=v1, k2=v2}, birth=Sat Jan 01 00:00:00 CST 2000)

2、加载指定配置文件

  • @PropertySource:加载指定的配置文件
  • @ConfigurationProperties:默认从全局配置文件中获取值
  1. 在resources目录下新建一个person.properties文件
name=五六七
  1. 将值注入到Person类中
@Component@Data@AllArgsConstructor@NoArgsConstructor// 加载指定的配置文件@PropertySource("classpath:person.properties")public class Person {    @Value("${name}")    private String name;    private Integer age;    private Dog dog;    private List<String> hobby;    private Map<String,Object> maps;    private Date birth;}

通过@PropertySource("classpath:person.properties")加载指定的配置文件

通过@Value("${name}")和EL表达式将值注入到类中

  1. 测试
@SpringBootTestclass HelloSpringBootApplicationTests {    @Autowired    Person person;    @Test    void contextLoads() {        System.out.println(person);    }}

输出:

Person(name=五六七, age=null, dog=null, hobby=null, maps=null, birth=null)

4.5、功能对比图

@ConfigurationProperties @Value
功能 批量注入到配置文件中的属性 一个个指定
松散绑定(松散语法) 支持 不支持
SpEL(Spring表达式语言) 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  • @ConfigurationProperties 只需要写一次即可,@Value则需要每个字段都添加。
  • 松散绑定:比如yaml中写的是last-name,这个和lastName是一样的。
  • JSR303校验:这个就是我们可以在字段上增加一层过滤器校验,可以保证数据的合法性
  • 复杂类型封装:yaml可以封装对象,使用@Value就不支持

5、JSR303校验

JSR303是Java EE6的一项子规范,在spring-boot中可以用@Validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。这里写个注解让name属性只能支持email格式。

  1. 首先在pom.xml中导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
  1. 在Person中增加注解
@Component@Data@AllArgsConstructor@NoArgsConstructor@ConfigurationProperties(prefix = "person")@Validated // 数据校验public class Person {    @Email(message = "邮箱格式错误")    private String name;    private Integer age;    private Dog dog;    private List<String> hobby;    private Map<String,Object> maps;    private Date birth;}

若name属性不符合Email格式,则会抛出如下错误

Property: person.nameValue: gmtOrigin: class path resource [application.yaml] - 2:9Reason: 邮箱格式错误

Bean Validation中内置的constraint

6、多环境切换

我们在编写主配置文件的时候,文件名可以使application-{profile}.properties/yaml,用来指定多个环境版本

1、多配置文件

application-test.properties代表测试环境配置

application-dev.properties代表开发环境配置

application.properties代表默认开发环境配置

但是Spring Boot并不会直接启动这些配置文件,它默认使用application.properties主配置文件

我们需要通过一个配置来选择需要激活的环境:

编写application.properties配置文件:

#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试#这时候启动程序,服务器端口就会变成8082spring.profiles.active=devserver.port=8080

application-test.properties配置文件:

server.port=8081

application-dev.properties配置文件:

server.port=8082

2、yaml的多文档块

server:  port: 8080# 选择要激活哪个环境块spring:  profiles:    active: dev---server:  port: 8081spring:  config:    activate:      on-profile: dev---server:  port: 8082spring:  config:    activate:      on-profile: test

注意:如果yaml和properties同时配置了端口,并且没有激活其他环境,默认会使用properties配置文件

3、配置文件加载位置

Spring Boot启动会扫描一下位置的application.properties或者application.yaml文件作为Spring Boot的默认主配置文件:

  1. optional:classpath:/
  2. optional:classpath:/config/
  3. optional:file:./
  4. optional:file:./config/
  5. optional:file:./config/*/

注意:优先级是从低到高的

file是指项目根路径,和src目录同级

classpath是指java或者resources目录下

posted @ 2021-10-12 16:34  Cherish486  阅读(59)  评论(0编辑  收藏  举报