SpringBoot入门

一、了解SpringBoot

1.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整合了所有的框架 。

1.2、主要优点

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

二、第一个SpringBoot程序

2.1、SpringBoot项目创建方式

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

将SpringBoot项目打包,打包成功(“Build Success”)会生成对应的jar包,然后调用管理员cmd直接执行打包的jar包,即可完成项目部署(友情提示可以shift+鼠标右键显示在此打开powershell窗口
在这里插入图片描述

方式二:直接使用idea创建

在这里插入图片描述

彩蛋:

可以通过https://www.bootschool.net/ascii来更改SpringBoot项目启动时的运行图案:

  • 到项目下的 resources 目录下新建一个banner.txt 即可。

三、原理探究

3.1、Pom.xml文件

在这里插入图片描述

父依赖

​ pom.xml文件中,有一个父依赖(也可以说是该pom.xml文件依赖一个父项目),主要是管理项目的资源过滤和插件!

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

​ 点进去是个名为spring-boot-starter-parent 的pom文件,发现该项目也依赖一个父项目(名为spring-boot-dependencies

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

​ 这里才是真正的管理SpringBoot应用里面所有依赖版本的位置,SpringBoot的版本控制中心

​ 之后我们导入依赖默认是不需要写版本号的,但是如果导入的包没有在依赖管理中,就需要我们手动来进行版本的配置了

启动器(spring-boot-starter-xx)

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
  • spring-boot-starter-xx:spring-boot的场景启动器
  • spring-boot-starter-web:帮助我们导入web模块正常运行所依赖的组件
  • SpringBoot将所有的功能场景都抽取出来,做成一个个start(启动器),只需要在项目中引入这些start即可,所有的依赖都会导入进来;我们要使用什么功能就导入深夜场景的启动器即可;我们也可以自定义start启动器

3.2、主启动类

在这里插入图片描述

默认的主启动类:SpringBootFirstApplication

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootFirstApplication {

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

}

@SpringBootApplication

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

​ 点进注解:@SpringBootApplication==>

//元注解
@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}
  )}
)
public @interface SpringBootApplication {}

@SpringBootConfiguration

​ 作用:SpringBoot的配置类,标注在哪个类上,表示的哪个就是SpringBoot的配置类

​ @SpringBootConfiguration==>(进入@SpringBootConfiguration注解里面)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

@Configuration
@Indexed
public @interface SpringBootConfiguration {}

@Configuration

  • 这里的@Configuration,说明这相当于是一个Spring的xml配置文件

    @Configuration==>(进入@Configuration注解里面)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

@Component
public @interface Configuration {}

@Component

​ 这里的@Component,说明启动类本身也是一个组件,负责启动应用

@EnableAutoConfiguration

  • 开启自动配置功能(以前我们需要手动配置,现在SpringBoot可以帮我们自动配置)

  • @EnableAutoConfiguration注解“通知”SpringBoot开启自动配置功能,这样自动配置功能才能生效

    @EnableAutoConfiguration==>

@AutoConfigurationPackage(自动配置包)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

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

AutoConfigurationImportSelector类

​ 1、AutoConfigurationImportSelector类中有一个这样的方法

//获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  //这里的getSpringFactoriesLoaderFactoryClass返回的就是我们最开始看的启动自动导入配置文件的注解类:@EnableAutoConfiguration
  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;
}

​ 2、这个方法调用了SpringFactoriesLoader类的静态方法,我们进入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();
  //这里它又调用loadSpringFactories方法
  return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

​ 3、我们进入loadSpringFactories方法

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  //获取classloader,我们这里得到的就是@EnableAutoConfiguration标注的类本身
  Map<String, List<String>> result = (Map)cache.get(classLoader);
  if (result != null) {
    return result;
  } else {
    HashMap result = new HashMap();

    try {
      //取获取一个资源:"META-INF/spring.factories"
      Enumeration urls = classLoader.getResources("META-INF/spring.factories");
	//将读取到的资源遍历,封装成为一个Properties
      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);
    }
  }
}

4、发现多次出现:spring.factories

@AutoConfigurationPackage==>

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}
  • Registrar.class:将主动类所在包及包下面所有子包里面的所有组件扫描到Spring容器中

3.3、spring.factories

在这里插入图片描述

根据源头打开spring.factories,看到很多自动配置的文件,这就是自动配置根源所在!

我们在上面的自动配置类里面随便打开一个,比如:WebMvcAutoConfiguration

在这里插入图片描述

可以看到这些都是JavaConfig配置类(就是Spring框架中,可以用Java的方式来配置Spring,代替Spring自带的application.xml配置文件)

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

总结:

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

3.4、SpringApplication

不简单的方法

​ 最初以为就运行一个main方法,没想到开启了一个服务

@SpringBootApplication
public class SpringBootFirstApplication {

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

SpringApplication.run分析

  • SpringApplication的实例化
  • run方法的执行

SpringApplication

​ 这个类主要做了一下四件事:

  • 推断应用的类型是普通的项目还是Web项目

  • 查找并加载所有可用初始化器 , 设置到initializers属性中

  • 找出所有的应用程序监听器,设置到listeners属性中

  • 推断并设置main方法的定义类,找到运行的主类

    SpringApplication类的构造器:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  this.sources = new LinkedHashSet();
  this.bannerMode = Mode.CONSOLE;
  this.logStartupInfo = true;
  this.addCommandLineProperties = true;
  this.addConversionService = true;
  this.headless = true;
  this.registerShutdownHook = true;
  this.additionalProfiles = Collections.emptySet();
  this.isCustomEnvironment = false;
  this.lazyInitialization = false;
  this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
  this.applicationStartup = ApplicationStartup.DEFAULT;
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
  this.webApplicationType = WebApplicationType.deduceFromClasspath();
  this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
  this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
  this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = this.deduceMainApplicationClass();
}

3.5、关于SpringBoot,谈谈你的理解?

  • 自动装配
  • run()(不要讲很深,讲解一下它的作为就可以,4点)

四、SpringBoot配置

4.1、什么是yaml

YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML *的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

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

  • application.properties

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

    • 语法结构:key:空格value

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

4.2、基本语法格式

  • 空格
  • 以缩进来控制层级关系
  • 属性和值的大小写都是十分敏感
  • 字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号

注意:

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

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

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

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

4.3、.yml文件注入配置文件

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

​ 原来给bean注入属性值的方式:

  • 属性名上方@Value

  • 加载指定的配置文件@PropertySource(value = "classpath:tengfei.properties") 在再属性名上方使用Spring的EL表达式@Value("${name}")

    现在使用.yaml文件+相应注解的方式来进行属性值的注入

person.yaml配置文件

# SpringBoot的这个配置文件,到底是用来配置什么的?
# 对象
student01:
  name: mtf
  age: 18
# 行内写法
student02: {name:mtf,age: 18}

# 数组
pets01:
  - dog
  - cat

pets02: [dog,cat]

person:
  name: "Ma \n tengfei"
  age: 18
  happy: true
  birth: 2000/06/14
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
    - movie
  dog:
    name: 旺财
    age: 3

Person实体类

@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> map;
    private List list;
    private Dog dog;
  	...
}

执行测试

@SpringBootTest
class SpringBoot02ConfigApplicationTests {
  @Autowired
  private Person person;
  @Test
  void contextLoads() {
    System.out.println(person.toString());
  }
}

注意:需要添加依赖,来防止使用@ConfigurationProperties(prefix = "person") 注解“爆红”

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

在这里插入图片描述

4.4、配置文件(.yaml)占位符

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

注意:如果使用.properties文字作为配置文件,需要修改.properties文件默认的编码格式为UTF-8

4.5、方式之间对比

在这里插入图片描述

  1. @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
  2. 松散绑定: 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的
  3. JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
  4. 复杂类型封装,yml中可以封装对象 , 使用value就不支持

总结:

  • 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
  • 如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties!

4.6、JSR303数据校验

如何使用

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

@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {

    @Email(message="邮箱格式错误") //name必须是邮箱格式
    private String name;
}
//报错:Reason: 不是一个合法的电子邮件地址

使用数据校验,可以保证数据的正确性

常见参数

@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       验证 DateCalendar 对象是否在当前时间之前  
@Future     验证 DateCalendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则

4.7、多环境切换

profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境;

1、配置文件建立位置

在这里插入图片描述

2、.properties配置文件的切换

在这里插入图片描述

3、.yaml配置文件的切换
  • 方式一:

    可以在SpringBoot的默认配置文件中加载其他环境的配置文件

    server:
      port: 8081
    
    # 可以选择需要激活哪个配置文件
    spring:
      profiles:
        active: test
    

    在这里插入图片描述

  • 方式二:

    可以在SpringBoot的默认的核心配置文件中配置多个环境

    server:
      port: 8080
    
    spring:
      profiles:
        active: web
    
    ---
    server:
      port: 8081
    spring:
      config:
        activate:
          on-profile: test
    
    ---
    server:
      port: 8082
    spring:
      config:
        activate:
          on-profile: web
    

    在这里插入图片描述

五、自动配置再理解

5.1、配置文件该怎么写

​ 在SpringBoot的配置文件中,给特定功能的属性赋值来实现自定义的功能;但是这些属性名(就是在配置过程中给出的属性名的提示)其实是来自XxProperties类中进行定义的,因此可以参照XxProperties相关类来了解相应属性名的含义

在这里插入图片描述

5.2、自动配置原理

在这里插入图片描述

​ 通过上传框到的,逐步进入底层源码,可以看到最后SpringFactoriesLoader 类里面定义了一个静态常量public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 值得注意的是META-INF/spring.factories这个文件最后被加载并转换成了Properties类型

我们通过文件名全局搜索找到了spring.factories文件:

在这里插入图片描述

然后随便点进一个熟悉的:HttpEncodingAutoConfiguration配置类

在这里插入图片描述

在这里插入图片描述

//部分代码

//出现这个@Configuration和@Bean标志着这个类是个配置类(JavaConfig)--学习Spring中,Java配置类代替Spring的配置文件
@Configuration(
  proxyBeanMethods = false
)

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

//Spring底层@Conditional注解
  //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
  //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
  type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
  prefix = "server.servlet.encoding",
  value = {"enabled"},
  matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
  private final Encoding properties;

  public HttpEncodingAutoConfiguration(ServerProperties properties) {
    this.properties = properties.getServlet().getEncoding();
  }

  @Bean
  判断容器没有这个组件?
  @ConditionalOnMissingBean
  public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
    return filter;
  }

总结:

  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类

在进入ServerProperties这个类:

在这里插入图片描述

这就是自动装配的大概流程!

狂神总结精华:

  • SpringBoot启动会加载大量的自动配置类
  • 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中
  • 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  • 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可
  • **xxxxAutoConfigurartion:自动配置类;**给容器中添加组件
  • xxxxProperties:封装配置文件中相关属性
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐