• 狂神。SpringBoot学习(1)


    SpringBoot学习

    我的springboot版本:2.7.2

    1、SpringBoot概述

    1.1、回顾什么是Spring

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

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

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

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

    1. 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

    2. 通过IOC,依赖注入(DI)和面向接口实现松耦合;

    3. 基于切面(AOP)和惯例进行声明式编程;

    4. 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;

    1.3、什么是SpringBoot

    SpringBoot 就是一个 javaweb 的开发框架,和 SpringMVC 类似,对比其他 javaweb 框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。

    所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景衍生一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。

    这就是Java企业级应用->J2EE->spring->springboot 的过程。

    随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;

    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配置的要求;

    1.4、微服务架构

    什么是微服务?

    微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须建成一系列小服务组合,可以通过http方式进行通信。

    单体应用架构:

    所谓单体应用架构(all in one)是指,我们将一个应用中的所有应用服务都封装在一个应用中。

    无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。

    • 这样做的好处是,易于开发和测试;也十分方便部署了;当需要扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了。
    • 单体应用架构的缺点时,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包。特别是对于一个大型应用,我们不可能把所有内容都放在一个应用里面,我们如何维护、如何分工合作都是问题。

    微服务架构:

    all in one 的架构方式,我们把所有的功能单元放在一个应用里面。然后我们把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后在负载均衡。

    所谓微服务架构,就是打破之前 all in one 的架构方式,把每个功能元素独立出来,把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些可以整合多个功能元素,所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。

    这样做的好处是:

    1. 节省了调用资源;
    2. 每个功能元素的服务都是一个可替换的,可独立升级的软件代码;
    3. 高内聚(在划分模块时,要把功能关系紧密的放到一个模块中);低耦合(模块之间的联系越少越好,接口越简单越好);

    在这里插入图片描述

    如何构建微服务:

    一个大型系统的微服务架构,就像一个复杂交织的神经网络,每一个神经元就是一个功能元素, 它们各自完成自己的功能,然后通过http相互请求调用。比如一个电商系统,查缓存、连数据库、浏览页面、结账、支付等服务都是一个个独立的功能服务,都被微化了,它们作为一个个微服务共同构建了一个庞大的系统。如果修改其中的一个功能,只需要更新升级其中一个功能服务单元即可。

    但是这种庞大的系统架构给部署和运维带来很大的难度。于是,spring为我们带来了构建大型分布式微服务的全套、全程产品:

    • 构建一个个功能独立的微服务应用单元,可以使用springboot,可以帮我们快速构建一个应用;
    • 大型分布式网络服务的调用,这部分springcloud来完成,实现分布式;
    • 在分布式中间,进行流式数据计算、批处理,我们有spring cloud data flow;
    • spring为我们想清楚了整个开始构建应用到大型分布式应用全流程方案;

    在这里插入图片描述

    2、第一个SpringBoot程序

    2.1、准备工作

    • Jdk 1.8
    • Maven 3.6.1
    • Springboot:最新版
    • 开发工具:IDEA

    2.2、创建基础项目说明

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

    Spring官方提供了非常方便的工具让我们快速构建应用:Spring Initializr:https://start.spring.io/

    1. 打开 https://start.spring.io/;

    2. 填写项目信息;

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

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

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

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

    1. 创建一个新项目;

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

    3. 填写项目信息;

    在这里插入图片描述

    1. 选择初始化的组件(初学勾选 Web 即可);

    在这里插入图片描述

    1. 填写项目路径;

    2. 等待项目构建成功;

    2.3、项目结构分析

    项目结构分析:

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

    1. 程序的主启动类 HelloworldApplication;

    2. 一个配置文件 application.properties;

    3. 一个 测试类 HelloworldApplicationTests;

    4. 一个 pom.xml;


    • 主启动类 HelloworldApplication:
    package com.kuang.helloworld;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    //本身就是spring的一个组件。
    
    //程序的主入口
    @SpringBootApplication
    public class HelloworldApplication {
    
        public static void main(String[] args) {
            //SpringApplication
            SpringApplication.run(HelloworldApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 配置文件 application.properties : (可以更改端口号)
    # springboot 的核心配置文件
    server.port=8081
    
    • 1
    • 2
    • 测试类 HelloworldApplicationTests:
    package com.kuang.helloworld;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    //单元测试
    @SpringBootTest
    class HelloworldApplicationTests {
    
        @Test
        void contextLoads() {
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 打开pom.xml,看看Spring Boot项目的依赖:
    
    <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.0modelVersion>
    
        
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.7.2version>
            <relativePath/> 
        parent>
    
        <groupId>com.kuanggroupId>
        <artifactId>helloworldartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>helloworldname>
        <description>Demo project for Spring Bootdescription>
        <properties>
            <java.version>1.8java.version>
        properties>
    
        <dependencies>
    
            
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    
        <build>
            
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                plugin>
            plugins>
        build>
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    2.4、编写一个http接口

    1. 在主程序的同级目录下,新建一个controller包。(一定要在同级目录下,否则识别不到)

    2. 在包中新建一个 HelloController 类:

    package com.kuang.helloworld.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    //自动装配,原理。
    @RestController
    public class HelloController {
    
        //接口:http://localhost:8080/hello
        @RequestMapping("/hello")
        public String hello(){
            //调用业务,接受前端的参数!
            return "hello,springboot!";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台也输出了 Tomcat 访问的端口号!

    在这里插入图片描述

    简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单。所以我们常用它来建立我们的微服务项目!

    2.5、打包项目

    将项目打成jar包,点击 maven 的 package。

    如果打包成功,则会在target目录下生成一个 jar 包。

    在这里插入图片描述

    打成了jar包后,就可以在任何地方运行了!

    2.6、彩蛋

    如何更改启动时显示的字符拼成的字母SpringBoot呢?也就是 banner 图案;

    只需一步:到项目下的 resources 目录下新建一个banner.txt 即可。

    图案可以到:https://www.bootschool.net/ascii 这个网站生成,然后拷贝到文件中即可!

    在这里插入图片描述

    3、运行原理初探

    3.1、pom.xml

    父依赖

    它主要依赖一个父项目,主要是管理项目的资源过滤及插件。

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.7.2version>
        <relativePath/> 
    parent>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    <parent>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-dependenciesartifactId>
      <version>2.7.2version>
    parent>
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了。

    启动器 spring-boot-starter

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

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

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

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

    3.2、主启动类

    默认的主启动类

    package com.kuang;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    //SpringBootApplication:标注这个类是一个SpringBoot的应用。
    @SpringBootApplication
    public class Springboot02ConfigApplication {
    
        public static void main(String[] args) {
            //将SpringBoot应用启动。
            SpringApplication.run(Springboot02ConfigApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    但是**一个简单的启动类并不简单!**我们来分析一下这些注解都干了什么。

    @SpringBootApplication

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

    进入这个注解:可以看到上面还有很多其他注解!

    在这里插入图片描述

    @ComponentScan

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

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

    @SpringBootConfiguration

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

    我们点击进去查看这个注解:

    @Configuration
    public @interface SpringBootConfiguration {}
    
    //再点击 @Configuration 进去看看
    @Component
    public @interface Configuration {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

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

    然后,我们回到 SpringBootApplication 注解中继续看。

    @EnableAutoConfiguration

    @EnableAutoConfiguration :开启自动配置功能

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

    点进去继续查看:

    @AutoConfigurationPackage :自动配置包

    再点进去查看:

    @Import(AutoConfigurationPackages.Registrar.class)

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

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

    然后,我们退到上一步,继续看:

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

    AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?

    我们点击去这个类看源码:

    1. 这个类中有一个这样的方法:
    //获得候选的配置
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      //这里的getSpringFactoriesLoaderFactoryClass()方法
      //返回的就是我们最开始看的启动自动导入配置文件的注解类:EnableAutoConfiguration
      List<String> configurations = new ArrayList<>(
          SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
      ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
      Assert.notEmpty(configurations,
          "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
              + "are using a custom packaging, make sure that file is correct.");
      return configurations;
    }
    
    
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
      return EnableAutoConfiguration.class;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 这个方法还调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法,点进去查看:
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        //这里它又调用了 loadSpringFactories 方法
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 我们继续点击查看 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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    1. 然后我们搜索 spring.factories 这个文件。

    spring.factories

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

    在这里插入图片描述

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

    (我在spring.factories没找到,在下面org包中找到的)

    在这里插入图片描述

    可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean。

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

    结论:

    springboot所有自动配置都是在启动的时候扫描并加载: spring.factories。所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!

    1. springboot在启动的时候,从类路径下/META-INF/spring. factories获取指定的值;
    2. 将这些自动配置的类导入容器,自动配置就会生效,帮我进行自动配置!
    3. 以前我们需要自动配置的东西,现在springboot帮我们做了!
    4. 整合javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下;
    5. 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
    6. 容器中也会存在非常多的 xxxAutoConfiguration 的文件(@Bean),就是这些类给容器中导入了这个场景需要
      的所有组件,并自动配置,@Configuration, JavaConfig!
    7. 有了自动配置类,免去了我们手动编写配置文件的工作!

    3.3、SpringApplication

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

    SpringApplication.run分析:

    分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行。

    SpringApplication

    这个类主要做了以下四件事情:

    1. 推断应用的类型是普通的项目还是Web项目。

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

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

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

    查看构造器:

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        // ......
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances();
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    run方法流程分析:

    在这里插入图片描述

    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>
    
    • 1
    • 2
    • 3

    yaml配置:

    server:
      prot: 8080
    
    • 1
    • 2

    4.2、yaml基础语法

    properties语法:

    # properties 只能保存键值对
    
    name = kuangshen
    student.name = kuangshen
    student.age = kuangshen
    
    • 1
    • 2
    • 3
    • 4
    • 5

    yaml语法:

    # 对空格的要求十分高!
    # 注入到我们的配置类中!
    
    # k-v
    name: kuangshen
    # 对象
    student:
      name: kuangshen
      age: 3
    # 行内写法
    student2: {name: kuangshen,age: 3}
    # 数组
    pets:
      - cat
      - dog
      - pig
    pets2: [cat,dog,pig]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    说明:语法要求严格!

    1. 空格不能省略;

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

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

    4.3、yaml注入配置文件

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

    1. 在项目中的 resources 目录下新建一个文件 application.yaml 配置文件。

    2. 编写一个 Dog 实体类。并用 @Value 给bean注入属性值。

    package com.kuang.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Component   //注册bean到容器中
    public class Dog {
    
        @Value("旺财")
        private String dogName;
        @Value("3")
        private Integer age;
    
        public Dog() {
        }
    
        public Dog(String dogName, Integer age) {
            this.dogName = dogName;
            this.age = age;
        }
    
        public String getDogName() {
            return dogName;
        }
    
        public void setDogName(String dogName) {
            this.dogName = dogName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "dogName='" + dogName + '\'' +
                    ", age=" + age +
                    '}';
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    1. 在SpringBoot的测试类下查看注入的输出。
    package com.kuang;
    
    import com.kuang.pojo.Dog;
    import com.kuang.pojo.Person;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class Springboot02ConfigApplicationTests {
      
        @Autowired   //将狗的类自动注入进来。
        private Dog dog;
    
        @Test
        void contextLoads() {
            System.out.println(dog);
        }
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 查看结果,@Value注入成功!这是我们原来的办法。

    2. 我们再编写一个复杂一点的实体类:Person 类。

    package com.kuang.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    
    import javax.validation.constraints.Email;
    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 birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;
    
        public Person() {
        }
    
        public Person(String name, Integer age, Boolean happy, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
            this.name = name;
            this.age = age;
            this.happy = happy;
            this.birth = birth;
            this.maps = maps;
            this.lists = lists;
            this.dog = dog;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Boolean getHappy() {
            return happy;
        }
    
        public void setHappy(Boolean happy) {
            this.happy = happy;
        }
    
        public Date getBirth() {
            return birth;
        }
    
        public void setBirth(Date birth) {
            this.birth = birth;
        }
    
        public Map<String, Object> getMaps() {
            return maps;
        }
    
        public void setMaps(Map<String, Object> maps) {
            this.maps = maps;
        }
    
        public List<Object> getLists() {
            return lists;
        }
    
        public void setLists(List<Object> lists) {
            this.lists = lists;
        }
    
        public Dog getDog() {
            return dog;
        }
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", happy=" + happy +
                    ", birth=" + birth +
                    ", maps=" + maps +
                    ", lists=" + lists +
                    ", dog=" + dog +
                    '}';
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    1. 我们来使用yaml配置的方式进行注入,编写一个yaml配置!
    person:
      name: kuangshen
      age: 3
      happy: false
      birth: 2000/01/01
      maps: {k1: v1,k2: v2}
      lists:
       - code
       - girl
       - music
      dog:
        name: 旺财
        age: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 我们刚才已经把person这个对象的所有值都写好了,然后通过上面代码中的以下操作来注入到我们的类中!
    /*
    @ConfigurationProperties作用:
    将配置文件中配置的每一个属性的值,映射到这个组件中;
    告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
    参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应。
    */
    @ConfigurationProperties(prefix = "person")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. IDEA 提示springboot配置注解处理器没有找到。我们点开查看文档,根据文档的提示,添加一个依赖!
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-configuration-processorartifactId>
        <optional>trueoptional>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 确认以上配置都OK之后,我们去测试类中测试一下:
    package com.kuang;
    
    import com.kuang.pojo.Dog;
    import com.kuang.pojo.Person;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class Springboot02ConfigApplicationTests {
    
        @Autowired
        private Person person;
    
        @Test
        void contextLoads() {
            System.out.println(person);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 查看输出结果,所有值全部注入成功!

    4.4、加载指定的配置文件

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

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

    1. 我们去在resources目录下新建一个 kuang.properties文件。
    name=kuangshen
    
    • 1
    1. 然后在我们的代码中指定加载 kuang.properties 文件。
    @Component
    @PropertySource(value = "classpath:kuang.properties")
    public class Person {
    
        @Value("${name}")  //SPEL表达式取出配置文件的值。
        private String name;
        ......
          
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 再次测试,查看结果!指定配置文件绑定成功!

    注意:

    properties 配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8:

    在这里插入图片描述

    4.5、配置文件占位符

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

    person:
      name: kuangshen${random.uuid}   # 随机的uuid
      age: ${random.int}   # 随机的id
      happy: false
      birth: 1999/12/23
      maps: {k1: v1,k2: v2}
      hello: hello!
      lists:
        - code
        - music
        - girl
      dog:
        # 如果配置文件中有person.hello这个属性,就会输出这个属性;如果没有的话,会输出hello。
        name: ${person.hello:hello}-旺财
        age: 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.6、对比小结

    @Value 这个使用起来并不友好!我们需要为每个属性单独注解赋值,比较麻烦,我们来看个功能对比图:

    在这里插入图片描述

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

    2. 松散绑定:比如我的yaml中写的 last-name,这个和 lastName 是一样的,后面跟着的字母默认是大写的。这就是松散绑定。

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

    4. 复杂类型封装,yaml中可以封装对象 , 使用@Value就不支持;

    结论:

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

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

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

    5、JSR303数据校验及多环境切换

    5.1、JSR303数据校验的使用

    Springboot中可以用 @validated 来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

    我们这里来写个注解让我们的name只能支持Email格式。

    @Component
    @ConfigurationProperties(prefix = "person")
    @Validated   //数据校验
    public class Person {
    
        @Email(message = "邮箱格式错误!")   //name必须是邮箱格式,f否则会提示message。
        private String name;
    		......
          
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查看运行结果 :

    在这里插入图片描述

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

    注意:

    SpringBoot 2.3.0版本之后就没有引入validation对应的包,程序包javax.validation不存在,需要自己手动导入!

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-validationartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.2、JSR303数据校验常见参数

    @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 对象是否符合正则表达式的规则
    
    .......等等
    除此以外,我们还可以自定义一些数据校验规则
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5.3、多环境切换

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

    多配置文件

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

    例如:

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

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

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

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

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

    yaml的多文档块

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

    server:
      port: 8088
    #选择要激活那个环境块
    spring:
      profiles:
        active: dev
    
    ---
    server:
      port: 8089
    spring:
      profiles: dev  # 配置环境的名称
    
    ---
    server:
      port: 8090
    spring:
      profiles: test  # 配置环境的名称
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

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

    配置文件加载位置

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

    在这里插入图片描述

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

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

    优先级由高到底,高优先级的配置会覆盖低优先级的配置。

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

    6、自动配置原理

    6.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;
        }
        //。。。。。。。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

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

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

    我们去配置文件里面试试前缀,看提示!

    在这里插入图片描述

    精髓:

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

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

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

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

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

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

    6.2、了解@Conditional

    了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效。

    @Conditional派生注解(Spring注解版原生的@Conditional作用)

    作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才生效。

    在这里插入图片描述

    那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

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

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

    #开启springboot的调试类
    debug=true
    
    • 1
    • 2

    Positive matches:(自动配置类启用的:正匹配)

    Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

    Unconditional classes: (没有条件的类)

    【演示:查看输出的日志】

    7、SpringBoot Web开发

    7.1、静态资源处理

    我们的项目中有许多的静态资源,比如css,js等文件,这个SpringBoot怎么处理呢?

    如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在这里面的。但是我们现在的pom呢,打包方式是为jar的方式,那么这种方式SpringBoot能不能来给我们写页面呢?当然是可以的,但是SpringBoot对于静态资源放置的位置,是有规定的!

    第一种静态资源映射规则:

    在SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面。我们可以去看看 WebMvcAutoConfigurationAdapter 中有很多配置方法。

    其中有一个方法:addResourceHandlers(添加资源处理):

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
      if (!this.resourceProperties.isAddMappings()) {
        // 已禁用默认资源处理
        logger.debug("Default resource handling disabled");
        return;
      }
      // webjars 配置
      addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
      addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
          ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
          registration.addResourceLocations(resource);
        }
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    读一下源代码:比如所有的 /webjars/**, 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源。

    什么是webjars 呢?

    Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。

    使用SpringBoot需要使用Webjars,我们可以去搜索一下:网站:https://www.webjars.org

    要使用jQuery,我们只要引入jQuery对应版本的pom依赖即可:

    
    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>jqueryartifactId>
        <version>3.4.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    导入完毕后,查看webjars目录结构:

    在这里插入图片描述

    只要是静态资源,SpringBoot就会去对应的路径寻找资源。

    要访问Jquery.js文件,我们这里就访问:http://localhost:8080/webjars/jquery/3.4.1/jquery.js

    在这里插入图片描述

    第二种静态资源映射规则:

    那我们项目中要是使用自己的静态资源该怎么导入呢?我们去上面所述代码中的看下一行代码。

    我们进入 getStaticLocations() 方法进一步分析:

    //在上面的代码中进入 getStaticLocations 方法。
    public String[] getStaticLocations() {
      return this.staticLocations;
    }
    
    //点击 staticLocations 找到对应的值。
    private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
    
    //再点击 CLASSPATH_RESOURCE_LOCATIONS 找到对应的路径。
    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 
      "classpath:/META-INF/resources/",
    	"classpath:/resources/", 
      "classpath:/static/", 
      "classpath:/public/" 
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里可以设置和我们静态资源有关的参数,这里面指向了它会去寻找资源的文件夹,即上面数组的内容。

    所以得出结论,以下四个目录存放的静态资源可以被我们识别:

    "classpath:/META-INF/resources/"
    "classpath:/resources/"
    "classpath:/static/"
    "classpath:/public/"
    
    • 1
    • 2
    • 3
    • 4

    我们可以在resources根目录下新建对应的文件夹,都可以存放我们的静态文件。

    比如:我们在对应的位置新建一个 1.js ,并访问 http://localhost:8080/1.js , 他就会去这些文件夹中寻找对应的静态资源文件:

    在这里插入图片描述

    通过测试,我们发现这几个位置的优先级为:

    resources > static > public
    
    • 1

    自定义静态资源路径:

    我们也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在application.properties中配置:

    这里研究了老半天才看明白😂

    # 指定了访问项目中的静态资源的url地址,需要以/static开头.   http://localhost:8080/static/1.js
    spring.mvc.static-path-pattern = /static/**
    # 指定静态资源的存放位置.   http://localhost:8080/1.js
    spring.web.resources.static-locations=classpath:/kuang/
    
    # 这两个配合使用,意思是输入 http://localhost:8080/static/1.js 访问了 kuang/1.js
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了!

    7.2、首页处理

    静态资源文件夹说完后,我们继续向下看源码!可以看到一个欢迎页的映射 welcomePageHandlerMapping ,就是我们的首页!

    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
      WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
          new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
          this.mvcProperties.getStaticPathPattern());
      welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
      welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
      return welcomePageHandlerMapping;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    点击 getWelcomePage() 进去看看:

    private Resource getWelcomePage() {
      for (String location : this.resourceProperties.getStaticLocations()) {
        Resource indexHtml = getIndexHtml(location);
        if (indexHtml != null) {
          return indexHtml;
        }
      }
      ServletContext servletContext = getServletContext();
      if (servletContext != null) {
        return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
      }
      return null;
    }
    
    // 欢迎页就是一个 location 下的 index.html 而已。
    private Resource getIndexHtml(String location) {
      return getIndexHtml(this.resourceLoader.getResource(location));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    欢迎页:静态资源文件夹下的所有 index.html 页面都被 /** 映射。

    比如我访问 http://localhost:8080/ ,就会找静态资源文件夹下的 index.html。

    新建一个 index.html ,在我们上面的3个目录中任意一个,然后访问测试 http://localhost:8080/ 就可以到我们的首页了!

    8、Thymeleaf模板引擎

    8.1、模板引擎

    前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。

    jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,像第二,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的

    那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?

    SpringBoot推荐你可以来使用模板引擎:

    模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的,什么样一个思想呢?我们来看一下这张图:

    在这里插入图片描述

    模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。只不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。其他的我就不介绍了,我主要来介绍一下SpringBoot给我们推荐的Thymeleaf模板擎,这模板引擎呢,是一个高级语言的模板引擎,他的这个语法更简单。而且呢,功能更强大。

    我们呢,就来看一下这个模板引擎,那既然要看这个模板引擎。首先,我们来看SpringBoot里边怎么用。

    8.2、引入Thymeleaf

    怎么引入呢,对于springboot来说,什么事情都是一个start的事情,我们去在项目中引入一下。

    给大家三个网址:

    Thymeleaf 官网:https://www.thymeleaf.org/

    Thymeleaf 在Github 的主页:https://github.com/thymeleaf/thymeleaf

    Spring官方文档:找到我们对应的版本:

    https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

    找到对应的pom依赖包,并start启动!

    
    <dependency>
        <groupId>org.thymeleafgroupId>
        <artifactId>thymeleaf-spring5artifactId>
    dependency>
    <dependency>
        <groupId>org.thymeleaf.extrasgroupId>
        <artifactId>thymeleaf-extras-java8timeartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Maven会自动下载jar包,我们可以去看下下载的东西:

    在这里插入图片描述

    8.3、Thymeleaf分析

    前面我们已经引入了Thymeleaf,那这个要怎么使用呢?

    我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,再按照那个规则,我们进行使用。

    我们去找一下Thymeleaf的自动配置类:ThymeleafProperties:

    @ConfigurationProperties(prefix = "spring.thymeleaf")
    public class ThymeleafProperties {
      
    	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
    	public static final String DEFAULT_PREFIX = "classpath:/templates/";
    	public static final String DEFAULT_SUFFIX = ".html";
    	private boolean checkTemplate = true;
    	private boolean checkTemplateLocation = true;
    	private String prefix = DEFAULT_PREFIX;
    	private String suffix = DEFAULT_SUFFIX;
    	private String mode = "HTML";
    	private Charset encoding = DEFAULT_ENCODING;
      ......
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们可以在其中看到默认的前缀和后缀!

    我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。

    使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!

    8.4、Thymeleaf 语法学习

    要学习语法,还是参考官网文档最为准确,我们找到对应的版本看一下。

    Thymeleaf 官网:https://www.thymeleaf.org/ ,简单看一下官网!我们去下载 Thymeleaf 的官方文档!

    测试代码:

    1. 编写一个 HelloController:
    package com.kuang.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.util.Arrays;
    
    //在templates目录下的所有资源,只能通过controller来跳转!
    //这个需要模版引擎的支持!
    @Controller
    public class IndexController {
    
        @RequestMapping("/test")
        public  String test(Model model){
            model.addAttribute("msg","

    hello,springboot!

    "
    ); model.addAttribute("users", Arrays.asList("kuang","shen")); return "test"; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。
    xmlns:th="http://www.thymeleaf.org
    
    • 1
    1. 编写一个测试页面 test.html 放在 templates 目录下:
    DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    
    
    
    <div th:text="${msg}">div>
    
    <div th:utext="${msg}">div>
    
    <hr>
    
    
    <h3 th:each="user:${users}" th:text="${user}">h3>
    <h3 th:each="user:${users}">[[${user}]]h3>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 启动项目测试,查看结果!

    在这里插入图片描述

    结论:只要需要使用 thymeleaf ,只需要导入对应的依赖,在html文件中导入命名空间的约束就可以了!我们将html放在我们的templates目录下即可。


    OK,入门搞定,我们来认真研习一下Thymeleaf的使用语法!

    1. 我们可以使用任意的 th:attr 来替换html中原生属性的值!

    在这里插入图片描述

    1. 我们能写哪些表达式呢?
    Simple expressions:(表达式语法)
    Variable Expressions: ${...}:获取变量值;OGNL;
        1)、获取对象的属性、调用方法
        2)、使用内置的基本对象:#18
             #ctx : the context object.
             #vars: the context variables.
             #locale : the context locale.
             #request : (only in Web Contexts) the HttpServletRequest object.
             #response : (only in Web Contexts) the HttpServletResponse object.
             #session : (only in Web Contexts) the HttpSession object.
             #servletContext : (only in Web Contexts) the ServletContext object.
    
        3)、内置的一些工具对象:
          #execInfo : information about the template being processed.
          #uris : methods for escaping parts of URLs/URIs
          #conversions : methods for executing the configured conversion service (if any).
          #dates : methods for java.util.Date objects: formatting, component extraction, etc.
          #calendars : analogous to #dates , but for java.util.Calendar objects.
          #numbers : methods for formatting numeric objects.
          #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
          #objects : methods for objects in general.
          #bools : methods for boolean evaluation.
          #arrays : methods for arrays.
          #lists : methods for lists.
          #sets : methods for sets.
          #maps : methods for maps.
          #aggregates : methods for creating aggregates on arrays or collections.
    ==================================================================================
    
      Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
      Message Expressions: #{...}:获取国际化内容
      Link URL Expressions: @{...}:定义URL;
      Fragment Expressions: ~{...}:片段引用表达式
    
    Literals(字面量)
          Text literals: 'one text' , 'Another one!' ,Number literals: 0 , 34 , 3.0 , 12.3 ,Boolean literals: true , false
          Null literal: null
          Literal tokens: one , sometext , main ,Text operations:(文本操作)
        String concatenation: +
        Literal substitutions: |The name is ${name}|
        
    Arithmetic operations:(数学运算)
        Binary operators: + , - , * , / , %
        Minus sign (unary operator): -
        
    Boolean operations:(布尔运算)
        Binary operators: and , or
        Boolean negation (unary operator): ! , not
        
    Comparisons and equality:(比较运算)
        Comparators: > , < , >= , <= ( gt , lt , ge , le )
        Equality operators: == , != ( eq , ne )
        
    Conditional operators:条件运算(三元运算符)
        If-then: (if) ? (then)
        If-then-else: (if) ? (then) : (else)
        Default: (value) ?: (defaultvalue)
        
    Special tokens:
        No-Operation: _
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    9、MVC自动配置原理

    9.1、官网阅读

    在进行项目编写前,我们还需要知道一个东西,就是SpringBoot对我们的SpringMVC还做了哪些配置,包括如何扩展,如何定制。只有把这些都搞清楚了,我们在之后使用才会更加得心应手。途径一:源码分析,途径二:官方文档!

    Spring MVC Auto-configuration
    // Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
    Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
    // 自动配置在Spring默认设置的基础上添加了以下功能:
    The auto-configuration adds the following features on top of Spring’s defaults:
    // 包含视图解析器
    Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
    // 支持静态资源文件夹的路径,以及webjars
    Support for serving static resources, including support for WebJars 
    // 自动注册了Converter:
    // 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
    // Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
    Automatic registration of Converter, GenericConverter, and Formatter beans.
    // HttpMessageConverters
    // SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
    Support for HttpMessageConverters (covered later in this document).
    // 定义错误代码生成规则的
    Automatic registration of MessageCodesResolver (covered later in this document).
    // 首页定制
    Static index.html support.
    // 图标定制
    Custom Favicon support (covered later in this document).
    // 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
    Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
    
    /*
    如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。
    如果希望提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
    */
    If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
    (interceptors, formatters, view controllers, and other features), you can add your own 
    @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
    custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
    ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
    
    // 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
    If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    我们来仔细对照,看一下它怎么实现的,它告诉我们SpringBoot已经帮我们自动配置好了SpringMVC,然后自动配置了哪些东西呢?

    9.2、ContentNegotiatingViewResolver 内容协商视图解析器

    自动配置了 ViewResolver ,就是我们之前学习的 SpringMVC 的视图解析器。

    即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

    我们去看看这里的源码:我们找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法:

    @Bean
    @ConditionalOnBean(ViewResolver.class)
    @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
    public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
      ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
      resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
      // ContentNegotiatingViewResolver uses all the other view resolvers to locate
      // a view so it should have a high precedence
      // ContentNegotiatingViewResolver 使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级.
      resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
      return resolver;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们可以点进这类看看!找到对应的解析视图的代码:

    @Nullable
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
        if (requestedMediaTypes != null) {
            // 获取候选的视图对象.
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            // 选择一个最适合的视图对象,然后把这个对象返回.
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                return bestView;
            }
        }
     		// ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们继续点进去看,他是怎么获得候选的视图的呢?

    getCandidateViews 中看到他是把所有的视图解析器拿来,进行while循环,挨个解析!

    Iterator var5 = this.viewResolvers.iterator();
    
    • 1

    所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的

    我们再去研究下他的组合逻辑,看到有个属性 viewResolvers,看看它是在哪里进行赋值的:

    protected void initServletContext(ServletContext servletContext) {
        // 这里它是从 beanFactory 工具中获取容器中的所有视图解析器.
        // ViewRescolver.class 把所有的视图解析器来组合的.
        Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
        ViewResolver viewResolver;
        if (this.viewResolvers == null) {
            this.viewResolvers = new ArrayList(matchingBeans.size());
          // ......
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    既然它是在容器中去找视图解析器,我们是否可以猜想,我们就可以去实现一个视图解析器了呢?

    我们可以自己给容器中去添加一个视图解析器,这个类就会帮我们自动的将它组合进来,我们去实现一下。


    测试代码:

    1. 我们去写一个视图解析器来试试:
    package com.kuang.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import java.util.Locale;
    
    //如果,你想diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配!
    //扩展 springmvc
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        //ViewResolver 实现了视图解析器接口的类,我们就可以把它看作视图解析器。
        @Bean
        public ViewResolver myViewResolver(){
            return new MyViewResolver();
        }
    
        //自定义了一个自己的视图解析器MyViewResolver。
        public static class MyViewResolver implements ViewResolver{
            @Override
            public View resolveViewName(String viewName, Locale locale) throws Exception {
                return null;
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    1. 怎么看我们自己写的视图解析器有没有起作用呢?

    我们给 DispatcherServlet 中的 doDispatch 方法加个断点进行调试一下,因为所有的请求都会走到这个方法中:

    在这里插入图片描述

    1. 我们启动我们的项目,然后随便访问一个页面,看一下Debug信息:

    找到this:

    在这里插入图片描述

    找到视图解析器,我们看到我们自己定义的就在这里了:

    在这里插入图片描述

    所以说,我们如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了!

    9.3、转换器和格式化器

    在 WebMvcAutoConfiguration 中找到格式化转换器:

    @Bean
    @Override
    public FormattingConversionService mvcConversionService() {
      Format format = this.mvcProperties.getFormat();
      WebConversionService conversionService = new WebConversionService(new DateTimeFormatters()
          .dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
      addFormatters(conversionService);
      return conversionService;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    点击去:

    private final Format format = new Format();
    
    public Format getFormat() {
      return this.format;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到在我们的Properties文件中,我们可以进行自动配置它!

    如果配置了自己的格式化方式,就会注册到Bean中生效,我们可以在配置文件中配置日期格式化的规则:

    # 自定义的配置日期格式化!
    spring.mvc.date-format=
    
    • 1
    • 2

    其余的就不一一举例了!

    9.4、修改SpringBoot的默认配置

    SpringBoot 在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的。如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!

    扩展使用SpringMVC:

    官方文档如下:

    If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
    
    • 1

    我们要做的就是编写一个 @Configuration 注解类,并且类型要为 WebMvcConfigurer,还不能标注@EnableWebMvc注解。

    我们去自己写一个,我们新建一个包叫config,写一个类MyMvcConfig2:

    package com.kuang.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    //如果我们要扩展springmvc,官方建议我们这样去做!
    @Configuration
    //@EnableWebMvc   //这玩意就是导入了一个类:DelegatingWebMvcConfiguration:从容器中获取所有的WebMvcConfigurer;
    public class MyMvcConfig2 implements WebMvcConfigurer {
    
        //视图跳转
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/kuang").setViewName("test");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们去浏览器访问一下,查看结果!

    确实也跳转过来了!所以说,我们要扩展SpringMVC,官方就推荐我们这么去使用,既保留SpringBoot所有的自动配置,也能用我们扩展的配置!

    我们可以去分析一下原理:

    1. WebMvcAutoConfiguration 是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter。

    2. 这个类上有一个注解,在做其他自动配置时会导入:@Import(EnableWebMvcConfiguration.class)。

    3. 我们点进 EnableWebMvcConfiguration 这个类看一下,它继承了一个父类:DelegatingWebMvcConfiguration。

    这个父类中有这样一段代码:

    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
      // 从容器中获取所有的webmvcConfigurer.
    	@Autowired(required = false)
    	public void setConfigurers(List<WebMvcConfigurer> configurers) {
    		if (!CollectionUtils.isEmpty(configurers)) {
    			this.configurers.addWebMvcConfigurers(configurers);
    		}
    	}
      // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 我们可以在这个类中去寻找一个我们刚才设置的 viewController 当做参考,发现它调用了一个addViewControllers() :
    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
      this.configurers.addViewControllers(registry);
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 我们点 addViewControllers() 进去看一下:
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
        // 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的.
        delegate.addViewControllers(registry);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    所以得出结论:所有的WebMvcConfiguration都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用。

    9.5、全面接管SpringMVC

    官方文档:

    If you want to take complete control of Spring MVC
    you can add your own @Configuration annotated with @EnableWebMvc.
    
    • 1
    • 2

    全面接管即:SpringBoot 对 SpringMVC 的自动配置不需要了,所有都是我们自己去配置!

    只需在我们的配置类中要加一个 @EnableWebMvc

    我们看下如果我们全面接管了SpringMVC了,我们之前SpringBoot给我们配置的静态资源映射一定会无效,我们可以去测试一下,查看结果!

    不加注解之前,访问首页,测试结果有效!

    给配置类加上注解:@EnableWebMvc,我们发现所有的SpringMVC自动配置都失效了!回归到了最初的样子。

    当然,我们开发中,不推荐使用全面接管SpringMVC。

    思考问题?为什么加了一个注解,自动配置就失效了!我们看下源码:

    1. 这里发现它是导入了一个类,我们可以继续进去看:
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }
    
    • 1
    • 2
    • 3
    1. 它继承了一个父类 WebMvcConfigurationSupport:
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
    
    • 1
    1. 我们来回顾一下 WebMvcAutoConfiguration 自动配置类 :
    @AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    		ValidationAutoConfiguration.class })
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    // 这个注解的意思就是:容器中没有这个组件的时候,这个自动配置类才生效.
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    public class WebMvcAutoConfiguration {
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    总结一句话:@EnableWebMvc 将 WebMvcConfigurationSupport 组件导入进来了.

    而导入的 WebMvcConfigurationSupport 只是 SpringMVC 最基本的功能!

    在SpringBoot中会有非常多的扩展配置(xxx Configuration),只要看见了这个,我们就应该多留心注意拓展了什么配置!

    员工管理系统

    接下来我们练习一个项目:

    员工管理系统

  • 相关阅读:
    昇思MindSpore再升级,深度科学计算的极致创新
    UNITY—2D游戏制作入门!
    React之初识fetch 通过fetch发送一个简单GET请求
    让我们用ArcGIS制作一张好看的中国月度气温图
    vue中的一个重要插件——vuex
    【云原生】设备云之内外组织与管理
    Academic Inquiry|创新与城市间知识溢出:社会、地理和技术的连通性和心理开放性
    食品饮料行业S2B2C系统:网站加速企业数字化转型,提高消费转化效率
    CMS之promotion failed&concurrent mode failure
    后端接口错误总结
  • 原文地址:https://blog.csdn.net/m0_56064238/article/details/126517855