SpringBoot学习笔记

163 阅读14分钟

1. 什么是SpringBoot

  Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

  简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。

  Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。

Spring Boot的主要优点:

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

2. HelloWorld

创建方式一: 使用Spring Initializr 的 Web页面创建项目

1、打开  start.spring.io/

2、填写项目信息

3、点击”Generate Project“按钮生成项目;下载此项目

4、解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。

5、如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。

创建方式二: 使用 IDEA 直接创建项目

1、创建一个新项目

2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现

3、填写项目信息

4、选择初始化的组件(初学勾选 Web 即可)

5、填写项目路径

6、等待项目构建成功

项目结构分析:

通过上面步骤完成了基础项目的创建。就会自动生成以下文件。

1、程序的主启动类

2、一个 application.properties 配置文件

3、一个 测试类

4、一个 pom.xml

编写一个接口:

  一定要在同级目录下,否则识别不到

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

  启动主启动类,然后再浏览器发送请求就可以看到输出。

image.png

2. SpringBoot的运行原理

2.1 pom.xml

父依赖

  从pom文件中可以看到

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

  ctrl+点击可以看到

<parent>    
   <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-dependencies</artifactId>    
    <version>2.2.5.RELEASE</version>    
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

  这里面管理着所有依赖版本,是SpringBoot的版本控制中心。也就是说导入依赖时默认是不需要指定版本,这样就避免了版本冲突的问题,但是如果导入的包没有在依赖中是需要手动配置版本的。

启动器 spring-boot-starter

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

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

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

  SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

2.2 主启动类

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {     
   //以为是启动了一个方法,没想到启动了一个服务
   SpringApplication.run(SpringbootApplication.class, args);   
   }
}

@SpringBootApplication:

  标注在某个类上,说明这是主启动类,SpringBoot应该运行这个类的main方法来启动SpringBoot应用。进入注解可以看到:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(    
    excludeFilters = {@Filter(    
    type = FilterType.CUSTOM,    
    classes = {TypeExcludeFilter.class}
), 
    @Filter(    
    type = FilterType.CUSTOM,    
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
    public @interface SpringBootApplication {    
    // ......
    }

@SpringBootConfiguration:

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

我们继续进去这个注解查看

// 点进去得到下面的 
@Component
@Configuration
public @interface SpringBootConfiguration {}
@Componentpublic @interface Configuration {}

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

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

@EnableAutoConfiguration:

作用:开启自动配置功能

  • 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置。
  • @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

@ComponentScan:

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

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

2.3 @EnableAutoConfiguration注解

  进入@EnableAutoConfiguration注解,可以看到:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

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

AutoConfigurationImportSelector :自动配置导入选择器,通过加载spring.factories文件来实现SpringBoot自动装配。

  进入@AutoConfigurationPackage注解,可以看到:

@Import({Registrar.class})
public @interface AutoConfigurationPackage {}

@import :Spring底层注解@import,给容器中导入一个组件。

Registrar.class:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器。

2.4 spring.factories

  打开看到了很多自动配置的文件;这就是自动配置根源所在。我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

image.png

  可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean。所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件,并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

3. 自动配置的原理

3.1 自动配置类

  以HttpEncodingAutoConfiguration(Http编码自动配置) 为例解释自动配置原理:

//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration

//启动指定类的ConfigurationProperties功能;
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class})

//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
        type = Type.SERVLET
)

//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({CharacterEncodingFilter.class})

//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
//如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
  //他已经和SpringBoot的配置文件映射了
  private final Encoding properties;
  //只有一个有参构造器的情况下,参数的值就会从容器中拿
  public HttpEncodingAutoConfiguration(HttpProperties properties) {
    this.properties = properties.getEncoding();
  }

  //给容器中添加一个组件,这个组件的某些值需要从properties中获取
  @Bean
  @ConditionalOnMissingBean //判断容器没有这个组件?
  public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
    return filter;
  }
  //。。。。。。。
}

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

  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类
//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {    // .....}

image.png

根据配置文件里面的配置,加载对应的配置。这些就是自动装配的原理!

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

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

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

2.2 @Conditional

  我们加载了这么多的配置类,但不是所有的都生效了。自动配置必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

4. yaml配置注入

4.1 yaml概述

  YAML是 "YAML Ain't a Markup Language" (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言) 这种语言以数据为中心,而不是以标记语言为重点!

  以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml。

传统xml配置:

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

yaml配置:

server:  prot: 8080

4.2 yaml语法

说明:语法要求严格!

1、空格不能省略

2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。

3、属性和值的大小写都是十分敏感的。

字面量:普通的值  [ 数字,布尔值,字符串  ]

字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;

k: v

注意:

  • “ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;

    比如 :name: "kuang \n shen"   输出 :kuang  换行   shen

  • '' 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出

    比如 :name: ‘kuang \n shen’   输出 :kuang  \n   shen

对象、Map(键值对)

#对象、Map格式
k:     
    v1:    
    v2:
    
#在下一行来写对象的属性和值得关系,注意缩进;比如:
student:    
    name: qinjiang    
    age: 3
    
#行内写法
student: {name: qinjiang,age: 3}

数组( List、set )

用 - 值表示数组中的一个元素,比如:

pets: 
    - cat 
    - dog 
    - pig
  
#行内写法
pets: [cat,dog,pig]

4.3 注入配置文件

导入springboot配置注解处理器依赖

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

编写yaml文件

person:  
    name: qinjiang  
    age: 3  
    happy: false  
    birth: 2000/01/01  
    maps: {k1: v1,k2: v2}  
    lists:   
        - code   
        - girl   
        - music  
    dog:    
        name: 旺财    
        age: 1

编写实体类

/*@ConfigurationProperties作用:将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定参数 prefix = “person” : 
将配置文件中的person下面的所有属性一一对应*/
@Component //注册bean
@ConfigurationProperties(prefix = "person")
public class Person {    
    private String name;    
    private Integer age;    
    private Boolean happy;    
    private Date birth;    
    private Map<String,Object> maps;    
    private List<Object> lists;    
    private Dog dog;
}

测试:

@SpringBootTestclass DemoApplicationTests {
    @Autowired    
    Person person; //将person自动注入进来
    @Test    
    public void contextLoads() {        
    System.out.println(person); //打印person信息    
    }
}

扩展: 配置文件还可以编写占位符生成随机数

person:    
    name: qinjiang${random.uuid} # 随机uuid    
    age: ${random.int}  # 随机int    
    happy: false    
    birth: 2000/01/01    
    maps: {k1: v1,k2: v2}    
    lists:      
        - code      
        - girl      
        - music    
    dog:      
        name: ${person.hello:other}_旺财      
        age: 1

4.4 加载指定文件

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

@PropertySource(value = "classpath:person.properties")

@ConfigurationProperties: 默认从全局配置文件中获取值;

4.5 @ConfigurationProperties和@Vaslue对比

1、@ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加

2、松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下

3、JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性

4、复杂类型封装,yml中可以封装对象 , 使用value就不支持

结论:

配置yml和配置properties都可以获取到值 , 强烈推荐 yml;

如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;

如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!

5. 数据校验以及多环境的切换

5.1 如何使用

  Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。使用数据校验,可以保证数据的正确性。 我们这里来写个注解让我们的name只能支持Email格式;

@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {
    @Email(message="邮箱格式错误") //name必须是邮箱格式    
    private String name;
    }

运行结果 :default message [不是一个合法的电子邮件地址];

5.2 常见参数

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;


空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.    Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false      长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.


日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前 
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

5.3 多环境的切换

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

例如:

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

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

  但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件, 我们需要通过一个配置来选择需要激活的环境:

#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev

yaml的多文档块

和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !

server:  
    port: 8081
#选择要激活那个环境块
spring:  
    profiles:    
        active: prod
    
---
server:  
    port: 8083
spring:  
    profiles: dev #配置环境的名称

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

配置文件的加载位置

  外部加载配置文件的方式十分多,我们选择最常用的即可,在开发的资源文件中进行配置!

  Springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:优先级由高到底,高优先级的配置会覆盖低优先级的配置。SpringBoot会从这四个位置全部加载主配置文件,互补配置

优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件

注意: 同一路径下优先级:properties > yaml > yml