springboot总(转狂神)

【笔记整理来源 B站狂神说https://www.bilibili.com/video/BV1PE411i7CV】

SpringBoot

SpringBoot 是用来简化Spring的,本质上和Spring是一致的, 核心点是自动装配

一、SpringBoot简介

1.1回顾什么是Spring

Spring是一个开源框架(就是一个容器,工厂),2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson。

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

1.2Spring是如何简化Java开发的

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

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

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

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

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

1.3什么是SpringBoot

学过javaweb的同学就知道,开发一个web应用,从最初开始接触Servlet结合Tomcat, 跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot,过一两年又会有其他web框架出现;你们有经历过框架不断的演进,然后自己开发项目所有的技术也在不断的变化、改造吗?建议都可以去经历一遍;

言归正传,什么是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配置的要求

真的很爽,我们快速去体验开发个接口的感觉吧!

二、微服务

2.1什么是微服务

  • 微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可以通过http的方式进行互通。要说微服务架构,先得说说过去我们的单体应用架构。

2.2单体应用架构

  • 所谓单体应用架构(all in one)是指,我们将一个应用的中的所有应用服务都封装在一个应用中。
  • 无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问, 等等各个功能放到一个war包内。(目前我们写的项目就是如此,将一个系统整体打包成war包)
  • 这样做的好处是,易于开发和测试;也十分方便部署;当需要扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了。
  • 单体应用架构的缺点是,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包。特别是对于一个大型应用,我们不可能把所有内容都放在一个应用里面,我们如何维护、如何分工合作都是问题。

2.3微服务架构

  • all in one的架构方式,我们把所有的功能单元放在一个应用里面。然后我们把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后在负载均衡。
  • 所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时,可以整合多个功能元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
  • 下这样做的好处是:
    • 节省了调用资源。
    • 每个功能元素的服务都是一个可替换的、可独立升级的软件代码。

在这里插入图片描述

  • Martin Flower于2014年3月25日写的《Microservices》 ,详细的阐述了什么是微服务。
  • 原文地址: http://martinfowler.com/articles/microservices.html
  • 翻译: https://www.cnblogs.com/liuning8023/p/4493156.html

2.4如何构建微服务

  • 一个大型系统的微服务架构,就像一个复杂交织的神经网络,每-个神经元就是一个功能元素, 它们各自完成自己的功能,然后通过http相互请求调用。比如一个电商系统,查缓存、连数据库、浏览页面、结账、支付等服务都是一个个独立的功能服务,都被微化了,它们作为一个个微服务共同构建了一个庞大的系统。如果修改其中的一个功能,只需要更新升级其中一个功能服务单元即可。
  • 但是这种庞大的系统架构给部署和运维带来很大的难度。于是,spring为我们带来了构建大型分布式微服务的全套、全程产品:
    • 构建一个个功能独立的微服务应用单元,可以使用springboot, 可以帮我们快速构建一个应用;
    • 大型分布式网络服务的调用,这部分由spring cloud来完成,实现分布式;
    • 在分布式中间,进行流式数据计算、批处理,我们有spring cloud data flow。
    • spring为我们想清楚了整个从开始构建应用到大型分布式应用全流程方案。

在这里插入图片描述

三、第一个SpringBoot程序

3.1准备工作

我们将学习如何快速的创建一个Spring Boot应用,并且实现一个简单的Http请求处理。通过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

我的环境准备:

  • java version “1.8”
  • Maven-3.6.1
  • SpringBoot 2.x 最新版

开发工具:

  • IDEA

3.2创建基础项目说明

Spring官方提供了非常方便的工具让我们快速构建应用(提供了一个快速生成的网站)

Spring Initializr:https://start.spring.io/

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

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

2、填写项目信息

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

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

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

项目创建方式二: 使用 IDEA 直接创建项目(idea继承了上面说的那个网站)

1、创建一个新项目

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

3、填写项目信息

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

5、填写项目路径

6、等待项目构建成功

项目结构分析:

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

1、程序的主启动类

2、一个 application.properties 配置文件

3、一个 测试类

4、一个 pom.xml

3.3pom.xml 分析

打开pom.xml,看看Spring Boot项目的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <!--有一个父项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wlw</groupId>
    <artifactId>hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello</name>
    <description>Demo project for Spring Boot</description>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>properties</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>java.version</span><span class="token punctuation">&gt;</span></span>1.8<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>java.version</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>properties</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependencies</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!--web依赖: 集成tomcat,配置dispatcherServlet,xml....--&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-boot-starter-web<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>

    <span class="token comment">&lt;!--所有的Springboot依赖都是使用这个前缀:spring-boot-starter--&gt;</span>

    <span class="token comment">&lt;!--单元测试--&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-boot-starter-test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>scope</span><span class="token punctuation">&gt;</span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>scope</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusions</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusion</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.junit.vintage<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>junit-vintage-engine<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusion</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusions</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependencies</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>build</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!--打jar包插件--&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugins</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-boot-maven-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugins</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>build</span><span class="token punctuation">&gt;</span></span>

</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
  • 52
  • 53
  • 54
  • 55
  • 56
  • 如上所示,主要有四个部分:
    • 项目元数据信息:创建时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括: groupld、 artifactld、 version、 name、 description等
    • parent:继承spring- boot -starter-parent的依赖管理,控制版本与打包等内容
    • dependencies: 项目具体依赖,这里包含了spring-boot-starter -web用于实现HTTP接口(该依赖中包含了Spring MVC),官网对它的描述是:使用Spring MVC构建Web (包括RESTful)应用程序的入门者,使用T omcat作为默认嵌入式容器。; spring-boot-starter-test用于编写单元测试的依赖包。更多功能模块的使用我们将在后面逐步展开。
    • build: 构建配置部分。默认使用了spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把Spring Boot应用打包成JAR来直接运行。

3.4编写一个http接口

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

2、在包中新建一个HelloController类

package com.wlw.hello.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//自动装配 : 这是原理!!!
//@Controller
@RestController
public class HelloController {
// 接口: http://localhost:8080/hello
@RequestMapping(“/hello”)
public String hello(){
//在此调用service ,接收前端的参数
return “hello,world!”;
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3、编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了 Tomcat 访问的端口号!

在这里插入图片描述

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

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

在这里插入图片描述

  • 如果遇到以上错误,可以配置打包时 跳过项目运行测试用例
<!--
    在工作中,很多情况下我们打包是不想执行测试用例的
    可能是测试用例不完事,或是测试用例会影响数据库数据
    跳过测试用例执
    -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--跳过项目运行测试用例-->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 如果打包成功,则会在target目录下生成一个 jar 包

在这里插入图片描述

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

在这里插入图片描述

3.6小技巧

  • 更改tomcat端口号 ( resources 目录下的application.properties 文件)

    #更改项目端口号
    server.port=8082
    
       
       
    • 1
    • 2
  • 更改启动时显示的字符拼成的字母:到项目下的 resources 目录下新建一个banner.txt 即可,将图案放在这个文件即可

四、SpringBoo原理初探【重点】

  • 我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;

4.1pom.xml文件

4.1.1父依赖

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

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

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

4.1.2启动器 spring-boot-starter
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 
 
  • 1
  • 2
  • 3
  • 4
  • springboot-boot-starter-xxx:就是spring-boot的场景启动器

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

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

4.2主启动类 (看注解)

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

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

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
4.2.1.1@ComponentScan
  • 这个注解在Spring中很重要 ,它对应XML配置中的元素。

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

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

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

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

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
  • 里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
  • 我们回到 SpringBootApplication 注解中继续看。
4.2.1.3@EnableAutoConfiguration
  • @EnableAutoConfiguration :开启自动配置功能

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

  • 点进注解接续查看:

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    .....
    }
    
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
4.2.1.3.1 @AutoConfigurationPackage :自动配置包

点进去之后是:

@Import(AutoConfigurationPackages.Registrar.class) //自动配置包注册
public @interface AutoConfigurationPackage {}

 
 
  • 1
  • 2

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

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

这个分析完了,退到上一步,继续看

4.2.1.3.2 @Import({AutoConfigurationImportSelector.class}) :给容器导入组件
  • AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

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

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert<span class="token punctuation">.</span><span class="token function">notEmpty</span><span class="token punctuation">(</span>configurations<span class="token punctuation">,</span> <span class="token string">"No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> configurations<span class="token punctuation">;</span>

}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2、这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
<span class="token comment">//这里它又调用了 loadSpringFactories 方法</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>List<span class="token punctuation">)</span><span class="token function">loadSpringFactories</span><span class="token punctuation">(</span>classLoader<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getOrDefault</span><span class="token punctuation">(</span>factoryClassName<span class="token punctuation">,</span> Collections<span class="token punctuation">.</span><span class="token function">emptyList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、我们继续点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();
            //将读取到的资源遍历,封装成为一个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 factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;
                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}
  • 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

4、发现一个多次出现的文件:spring.factories,全局搜索它 (这是自动配置的核心文件)

  • spring.factories

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

在这里插入图片描述

  • WebMvcAutoConfiguration

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

在这里插入图片描述

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

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

小结:SpringBoot所有自动配置都是在启动的时候扫描并加载:spring.factories配置文件 ,所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,而所谓的条件就是只要导入了对应的strater,就有对应的启动器了,有了启动器,条件成立,自动配置就生效,然后就配置成功!

4.2.2结论:
  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 ,以前我们需要自己配置的东西,现在SpringBoot 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在spring-boot-autoconfigure的jar包中,他会把所有需要导入的组件,以类名(全限定名)的方式返回,这些类名对应的组件就会被添加到容器里;
  4. 容器中有很多的xxxAutoConfiguration的文件(@Bean), 就是这些类给容器中导入这个场景需要的所有组件 , 并自动配置,@Configuration ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

现在大家应该大概的了解了下,SpringBoot的运行原理,后面我们还会深化一次!

4.3 SpringApplication(看类与方法)

不简单的方法

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • SpringApplication.run分析

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

4.3.1 SpringApplication类
  • 这个类主要做了以下四件事情:
    • 推断应用的类型是普通的项目还是Web项目
    • 查找并加载所有可用初始化器 , 设置到initializers属性中
    • 找出所有的应用程序监听器,设置到listeners属性中
    • 推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
<span class="token comment">// ......</span>

<span class="token keyword">this</span><span class="token punctuation">.</span>webApplicationType <span class="token operator">=</span> WebApplicationType<span class="token punctuation">.</span><span class="token function">deduceFromClasspath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setInitializers</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getSpringFactoriesInstances</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setListeners</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getSpringFactoriesInstances</span><span class="token punctuation">(</span>ApplicationListener<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">this</span><span class="token punctuation">.</span>mainApplicationClass <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">deduceMainApplicationClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
4.3.2 run方法流程分析

跟着源码和这幅图就可以一探究竟了!

在这里插入图片描述

五、yaml语法学习

5.1 SpringBoot的全局配置文件

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

  • 可以是 :application.properties

    • 语法结构 :key=value
  • 或者是:application.yaml (推荐使用)

    • 语法结构 :key:空格 value (空格必须存在

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

比如我们可以在配置文件(application.properties)中修改Tomcat 默认启动的端口号!测试一下!

server.port=8081

 
 
  • 1

或者 application.yaml :

server:
  port: 8082

 
 
  • 1
  • 2

5.2 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

5.3yaml基础语法

  • 说明:语法要求严格!

    • 空格不能省略
    • 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
    • 属性和值的大小写都是十分敏感的。
  • 字面量:普通的值 [ 数字,布尔值,字符串 ]

    • 字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;
#yaml格式(注意空格)
k: v

 
 
  • 1
  • 2
  • 注意:

    • “ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;比如 :name: “kuang \n shen” 输出 :kuang 换行 shen
    • ’ ’ 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出比如 :name: ‘kuang \n shen’ 输出 :kuang \n shen
  • 对象、Map(键值对)

#对象、Map格式
k:
 v1:    
 v2:

 
 
  • 1
  • 2
  • 3
  • 4
  • 在下一行来写对象的属性和值得关系,注意缩进;比如:
student:
 name: qinjiang    
 age: 3

 
 
  • 1
  • 2
  • 3
  • 行内写法
student: {name: qinjiang,age: 3}

 
 
  • 1
  • 数组( List、set )
    • 用 - 值表示数组中的一个元素,比如:
pets: 
 - cat 
 - dog 
 - pig

 
 
  • 1
  • 2
  • 3
  • 4
  • 行内写法
pets: [cat,dog,pig]

 
 
  • 1

总:

#yaml文件可以存储的东西,重点对于空格的要求很严格
# 普通的键值对
name: mengyuanshishabi

# 对象
student:
name: mengyuan
age: 3
#行内表示
user: { name: mengyuan,age: 3}

# 数组
pets:
- cat
- pig
- dog
# 行内表示
pet: [cat,dog,pig]

# 而properties只能存键值对 key=value
# yaml格式文件的另一个强大之处,在于可以注入到我们的配置类中,下面来说:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.4注入配置文件

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

1、在springboot项目中的resources目录下新建一个文件 application.yaml

2、编写一个实体类 Dog;

package com.wlw.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component //添加到spring 的组件里,注解生成<bean> ,在之前的开发中,也用注解来注入属性值
public class Dog {
private String name;
private Integer age;
//有参无参构造、get、set方法、toString()方法
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、思考,我们原来是如何给bean注入属性值的!@Value,给狗狗类测试一下:

@Component //注册bean
public class Dog {    
    @Value("旺财")
    private String name;
    @Value("3")
    private Integer age;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4、在SpringBoot的测试类下注入狗狗输出一下;

package com.wlw;

import com.wlw.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Springboot02ConfigApplicationTests {

<span class="token annotation punctuation">@Autowired</span>
<span class="token keyword">private</span> Dog dog<span class="token punctuation">;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">contextLoads</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>dog<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

结果成功输出,@Value注入成功,这是我们原来的办法对吧。

在这里插入图片描述

5、我们在编写一个复杂一点的实体类:Person 类

package com.wlw.pojo;

import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
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;
//有参无参构造、get、set方法、toString()方法
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

6、我们来使用yaml配置的方式进行注入,大家写的时候注意区别和优势,我们编写一个yaml配置!

person:
  name: qinjiang
  age: 3
  happy: false
  birth: 2000/01/01
  maps: {k1: v1,k2: v2}
  lists:
    - code
    - girl
    - music
  dog:
    name: 旺财
    age: 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

7、我们刚才已经把person这个对象的所有值都写好了,我们现在来注入到我们的类中!

/*
@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;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

8、IDEA 提示,springboot配置注解处理器没有找到,让我们看文档,我们可以查看文档,找到一个依赖!

在这里插入图片描述

在这里插入图片描述

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9、确认以上配置都OK之后,我们去测试类中测试一下:

package com.wlw;

import com.wlw.pojo.Dog;
import com.wlw.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 {

<span class="token annotation punctuation">@Autowired</span>
<span class="token keyword">private</span> Person person<span class="token punctuation">;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">contextLoads</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

结果:所有值全部注入成功!

在这里插入图片描述

yaml配置注入到实体类完全OK!

课堂测试:

1、将配置文件的key 值 和 属性的值设置为不一样,则结果输出为null,注入失败

2、在配置一个person2,然后将 @ConfigurationProperties(prefix = “person2”) 指向我们的person2;

5.4.2加载指定的配置文件

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

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

1、我们去在resources目录下新建一个person.properties文件

name=mengyuan

 
 
  • 1

2、然后在我们的代码中指定加载person.properties文件properties

@PropertySource(value = "classpath:person.properties")
@Component //注册bean
public class Person {
<span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${name}"</span><span class="token punctuation">)</span>
<span class="token keyword">private</span> String name<span class="token punctuation">;</span>

<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>  

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3、再次输出测试一下:指定配置文件绑定成功!

在这里插入图片描述

5.4.3配置文件占位符

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
5.4.4回顾properties配置

我们上面采用的yaml方法都是最简单的方式,开发中最常用的;也是springboot所推荐的!那我们来唠唠其他的实现方式,道理都是相同的;写还是那样写;配置文件除了yaml还有我们之前常用的properties , 我们没有讲,我们来唠唠!

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

settings–>FileEncodings 中配置;

在这里插入图片描述

测试步骤:

1、新建一个实体类User

@Component //注册bean
public class User {
    private String name;
    private int age;
    private String sex;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、编辑配置文件 user.properties

user.name=wlw
user.age=18
user.sex=男

 
 
  • 1
  • 2
  • 3

3、我们在User类上使用@Value来进行注入!

@Component //注册bean
@PropertySource(value = "classpath:user.properties")
public class User {
    //直接使用@value
    @Value("${user.name}") //从配置文件中取值
    private String name;
    @Value("#{9*2}")  // #{SPEL} Spring表达式
    private int age;
    @Value("男")  // 字面量
    private String sex;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4、Springboot测试

@SpringBootTest
class DemoApplicationTests {
<span class="token annotation punctuation">@Autowired</span>
User user<span class="token punctuation">;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">contextLoads</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}
/*
结果:
User(name=wlw,age=18,sex=男)
*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
5.4.5对比小结

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

在这里插入图片描述

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

2、松散绑定:这个什么意思呢? 比如我的yaml中写的last-name,这个和lastName是一样的, - 后面跟着的字母 默认是大写的。这就是松散绑定。可以测试一下 (yaml文件中写last-name,实体类中写lastName,是可以对 应起来的)

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

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

结论:

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

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

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

5.5JSR303数据校验

5.5.1先看看如何使用
  • Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式;
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {
<span class="token annotation punctuation">@Email</span><span class="token punctuation">(</span>message<span class="token operator">=</span><span class="token string">"邮箱格式错误"</span><span class="token punctuation">)</span> <span class="token comment">//name必须是邮箱格式</span>
<span class="token keyword">private</span> String name<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

5.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 对象是否符合正则表达式的规则

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

  • 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

六、多环境切换

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

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

例如:

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

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

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

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

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

 
 
  • 1
  • 2
  • 3
6.2yaml的多文档块

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

server:
  port: 8081
#选择要激活那个环境块
spring:
  profiles:
    active: prod


server:
port: 8083
spring:
profiles: dev #配置环境的名称

server:
port: 8084
spring:
profiles: prod #配置环境的名称

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

6.3配置文件加载位置

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

官方外部配置文件说明参考文档

在这里插入图片描述

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

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

 
 
  • 1
  • 2
  • 3
  • 4

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

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

我们在最低级的配置文件中设置一个项目访问路径的配置来测试互补问题;

#配置项目的访问路径
server.servlet.context-path=/kuang

 
 
  • 1
  • 2
6.4拓展,运维小技巧

指定位置加载配置文件

我们还可以通过spring.config.location来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.properti

 
 
  • 1

七、自动配置原理【重点】

  • 配置文件到底能写什么?怎么写?

  • SpringBoot官方文档中有大量的配置,我们无法全部记住

在这里插入图片描述

7.1分析自动配置原理

从spring.factories中选择一个类进入,我们以HttpEncodingAutoConfiguration(Http编码自动配置) 为例解释自动配置原理;

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

//启动指定类的ConfigurationProperties功能; (HttpProperties.class 这个就是自动配置类)
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
//并把HttpProperties加入到ioc容器中
//自动装配属性,装配好之后,HttpProperties.class这个里面的属性(要加上这个类注解绑定的前缀)就是我们在application.yaml配置文件中配置的键(key)
@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();
}

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

}

  • 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

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

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

在这里插入图片描述

这就是自动装配的原理!

7.2精髓

1、SpringBoot启动会加载大量的自动配置类(xxxxAutoConfigurartion),每一个自动配置类中都有对应的 xxxxProperties,这些xxxxProperties中有着相关的属性与默认值,这些属性又是和配置文件(application.yaml)绑定的,所以我们可以通过配置文件来修改SpringBoot自动配置的默认值;

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

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

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

xxxxAutoConfigurartion: 给容器中添加组件

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

7.3了解:@Conditional

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

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

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

在这里插入图片描述

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

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

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

#开启springboot的调试类
debug=true

 
 
  • 1
  • 2

输出的日志中大致分为一下几类:

  • Positive matches:(自动配置类启用的:正匹配) 已经启用了,并且生效的

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

  • Unconditional classes: (没有条件的类)

八、SpringBoot Web 开发前准备

  • 我们要解决的问题:
    • 导入静态资源,怎么样处理静态资源
    • 首页index.jsp/index.html
    • Jsp,模板引擎Thymeleaf
    • 装配扩展SpringMVC
    • 增删改查
    • 拦截器
    • 国际化

8.1处理静态资源

  • WebMvcAutoConfiguration.java 源码中关于静态资源的说明:

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //1.如果我们在配置文件中自己定义了路径,下面默认的就会失效
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
        }
    
    <span class="token comment">//2.导入一些静态jar包,如jquery,这些jar包的目录结构要求,如果我们访问http://localhost:8080/webjars/... 就可以访问到/webjars下面的资源</span>
    Duration cachePeriod <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>resourceProperties<span class="token punctuation">.</span><span class="token function">getCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPeriod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    CacheControl cacheControl <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>resourceProperties<span class="token punctuation">.</span><span class="token function">getCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCachecontrol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHttpCacheControl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>registry<span class="token punctuation">.</span><span class="token function">hasMappingForPattern</span><span class="token punctuation">(</span><span class="token string">"/webjars/**"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token function">customizeResourceHandlerRegistration</span><span class="token punctuation">(</span>registry<span class="token punctuation">.</span><span class="token function">addResourceHandler</span><span class="token punctuation">(</span><span class="token string">"/webjars/**"</span><span class="token punctuation">)</span>
                                             <span class="token punctuation">.</span><span class="token function">addResourceLocations</span><span class="token punctuation">(</span><span class="token string">"classpath:/META-INF/resources/webjars/"</span><span class="token punctuation">)</span>
                                             <span class="token punctuation">.</span><span class="token function">setCachePeriod</span><span class="token punctuation">(</span><span class="token function">getSeconds</span><span class="token punctuation">(</span>cachePeriod<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setCacheControl</span><span class="token punctuation">(</span>cacheControl<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token comment">//3.源码中:staticPathPattern = "/**"  ,this.resourceProperties.getStaticLocations() = { "classpath:/META-INF/resources/",</span>
    		<span class="token string">"classpath:/resources/"</span><span class="token punctuation">,</span> <span class="token string">"classpath:/static/"</span><span class="token punctuation">,</span> <span class="token string">"classpath:/public/"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    

//所以只要我们访问 http://localhost:8080/.... 它就会匹配到上述的这些目录,去这些目录下寻找资源
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}

  • 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
  • 总结:

    • 如果我们在配置文件(application.properties)中自己定义了路径,SpringBoot默认的路径就会失效,一般不建议这样做,因为它会直接return,代码不会往下走

      spring.mvc.static-path-pattern=classpath:/wlw/
      
          
          
      • 1
    • http://localhost:8080/webjars/… 这个路径可以访问到/webjars下面的资源,一般会用于导入的依赖jar包的访问(这些依赖要去 webjars的官方网站:https://www.webjars.org 去找),要求这些jar包的目录结构要求如下:

      在这里插入图片描述

      
      <dependency>
          <groupId>org.webjars</groupId>
          <artifactId>jquery</artifactId>
          <version>3.5.1</version>
      </dependency>
      
          
          
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 那我们项目中要是使用自己的静态资源该怎么导入呢? 我们去找staticPathPattern发现第二种映射规则 :/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类 ,这个类在里面定义了这些目录({ “classpath:/META-INF/resources/”,“classpath:/resources/”, “classpath:/static/”, “classpath:/public/” }) 所以平常我们访问 http://localhost:8080/… ,它就会匹配到上述的目录,去这些目录下寻找资源, 优先级:resources > static > public ,我们通常就把静态资源放在这个三个目录下,至于怎样放,看个人习惯,很少使用/webjars

    在这里插入图片描述

  • 8.2首页和图标如何定制

    • 首页一般为index.html,它也可以算是一种静态资源,所以当我们把它放在resources目录下的 resources ,static 或 public任意一个文件夹下,直接访问项目(http://localhost:8080/),就会跳转到这个index.html

    • 这个操作是如何实施的呢,看一下源码,还是WebMvcAutoConfiguration.java 中的:

      @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;
      }
      

    private Optional<Resource> getWelcomePage() {
    String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
    return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
    }

    private Resource getIndexHtml(String location) {
    return this.resourceLoader.getResource(location + “index.html”);
    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 我们看到resources目录下还有一个名为templates的文件夹,这里也可以放页面文件,只不过这个里面的文件只能通过controller来跳转访问,这个功能实现也需要摸板引擎(依赖)的支持,后续会说

  • 图标定制:与其他静态资源一样,Spring Boot在配置的静态内容位置中(还是那三个目录)查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。

    • 关闭SpringBoot默认图标

      #关闭默认图标
      spring.mvc.favicon.enabled=false
      
          
          
      • 1
      • 2
    • 自己放一个图标在静态资源目录下,我放在 public 目录下

    • 清除浏览器缓存!刷新网页,发现图标已经变成自己的了!

  • 8.3Thymeleaf模板引擎

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

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

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

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

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

    在这里插入图片描述

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

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

    8.3.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依赖:可以适当点进源码看下本来的包!

    		<!--thymeleaf-->
            <dependency>
                <groupId>org.thymeleaf</groupId>
                <artifactId>thymeleaf-spring5</artifactId>
            </dependency>
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-java8time</artifactId>
            </dependency>
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • Maven会自动下载jar包,我们可以去看下下载的东西;

    在这里插入图片描述

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

    • 我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。我们去找一下Thymeleaf的自动配置类:ThymeleafProperties

    @ConfigurationProperties(
        prefix = "spring.thymeleaf"
    )
    public class ThymeleafProperties {
        private static final Charset DEFAULT_ENCODING;
        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 = "classpath:/templates/";
        private String suffix = ".html";
        private String mode = "HTML";
        private Charset encoding;
    }
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 我们可以在其中看到默认的前缀和后缀!

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

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

    8.3.4测试

    1、编写一个IndexController

    package com.wlw.controller;
    

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;

    @Controller
    public class IndexController {

    <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/index"</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> String <span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
        <span class="token keyword">return</span> <span class="token string">"index"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、编写一个测试页面 index.html 放在 templates 目录下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <h1>首页</h1>
    </body>
    </html>
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、启动项目请求测试

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

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

    • 我们做个最简单的练习 :我们需要查出一些数据,在页面中展示

    1、修改测试请求,增加数据传输;

    @Controller
    public class TestController {
    
    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/test"</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> String <span class="token function">test</span><span class="token punctuation">(</span>Model model<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
        model<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"msg"</span><span class="token punctuation">,</span><span class="token string">"hello,SpeingBoot!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token string">"test"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。

    我们可以去官方文档的#3中看一下命名空间拿来过来:

    <html lang="en" xml:th="http://www.thymeleaf.org" xmlns:th="http://www.w3.org/1999/xhtml">
    <!--命名空间的约束: 前一个是官方给的,后一个是改错自己加入的-->
    
     
     
    • 1
    • 2

    3、我们去编写下前端页面

    <!DOCTYPE html>
    <html lang="en" xml:th="http://www.thymeleaf.org" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
    
    <span class="token comment">&lt;!--所有的html元素都可以被thymeleaf替换接管, th:元素名称 --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${msg}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    

    </body>
    </html>

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4、启动测试!

    在这里插入图片描述

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

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

    在这里插入图片描述

    2、我们能写哪些表达式呢?

    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

    练习测试:

    1、 我们编写一个Controller,放一些数据

    @Controller
    public class TestController {
    
    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/test"</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> String <span class="token function">test</span><span class="token punctuation">(</span>Model model<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
        model<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"msg1"</span><span class="token punctuation">,</span><span class="token string">"&lt;h1&gt;hello,SpeingBoot!&lt;/h1&gt;"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        model<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"msg2"</span><span class="token punctuation">,</span><span class="token string">"&lt;h1&gt;hello,SpeingBoot!&lt;/h1&gt;"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        model<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"users"</span><span class="token punctuation">,</span> Arrays<span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span><span class="token string">"wlw"</span><span class="token punctuation">,</span><span class="token string">"hello"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token string">"test"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2、测试页面取出数据

    <!DOCTYPE html>
    <html lang="en" xml:th="http://www.thymeleaf.org" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
    
    <span class="token comment">&lt;!--所有的html元素都可以被thymeleaf替换接管, th:元素名称 --&gt;</span>
    
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${msg1}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span> <span class="token comment">&lt;!--转义特殊字符--&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>utext</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${msg2}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token comment">&lt;!--不转义特殊字符--&gt;</span>
    
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>hr</span><span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user:${users}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user:${users}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>[[${user}]]<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
    

    </body>
    </html>

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、启动项目测试!

    我们看完语法,很多样式,我们即使现在学习了,也会忘记,所以我们在学习过程中,需要使用什么,根据官方文档来查询,才是最重要的,要熟练使用官方文档!

    8.4SpringMVC自动配置原理

    8.4.1官网阅读
    • 在进行项目编写前,我们还需要知道一个东西,就是SpringBoot对我们的SpringMVC还做了哪些配置,包括如何扩展,如何定制。

    • 只有把这些都搞清楚了,我们在之后使用才会更加得心应手。途径一:源码分析,途径二:官方文档!

    • 地址 :https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

    //Spring MVC自动配置
    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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

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

    8.4.2ContentNegotiatingViewResolver 内容协商视图解析器

    自动配置了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使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
        resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return resolver;
    }
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    @Nullable // 注解说明:@Nullable 即参数可为null
    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
    • 10

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

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

    1、我们在我们的主程序中去写一个视图解析器来试试;

    package com.wlw.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;

    @Configuration //这个注解表明这是扩展的SpringMVC
    public class MyMvcConfig implements WebMvcConfigurer {

    <span class="token comment">//ViewResolver 实现了视图解析器接口的类,我们就可以把它看作是视图解析器</span>
    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> ViewResolver <span class="token function">MyViewResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
        <span class="token keyword">return</span> <span class="token function">MyViewResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token comment">//自定义一个自己的视图解析器,MyViewResolver</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MyViewResolver</span> <span class="token keyword">implements</span> <span class="token class-name">ViewResolver</span> <span class="token punctuation">{<!-- --></span>
        <span class="token annotation punctuation">@Override</span>
        <span class="token keyword">public</span> View <span class="token function">resolveViewName</span><span class="token punctuation">(</span>String viewName<span class="token punctuation">,</span> Locale locale<span class="token punctuation">)</span> <span class="token keyword">throws</span> Exception <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">return</span> null<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    

    }

    • 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

    2、怎么看我们自己写的视图解析器有没有起作用呢?

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

    在这里插入图片描述

    3、我们启动我们的项目,然后随便访问一个页面,看一下Debug信息;

    找到this

    img

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

    在这里插入图片描述

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

    8.4.3转换器和格式化器
    • 在WebMvcAutoConfiguration.java中找到格式化转换器:
    @Bean
    @Override
    public FormattingConversionService mvcConversionService() {
        // 拿到配置文件中的格式化规则
        WebConversionService conversionService = 
            new WebConversionService(this.mvcProperties.getDateFormat());
        addFormatters(conversionService);
        return conversionService;
    }
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 点进去:
    public String getDateFormat() {
        return this.dateFormat;
    }
    

    /**

    • Date format to use. For instance, dd/MM/yyyy. 默认的
      */
      private String dateFormat;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 可以看到在我们的Properties文件中,我们可以进行自动配置它!

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

    在这里插入图片描述

    • 其余的就不一一举例了,大家可以下去多研究探讨即可!
    8.4.4修改SpringBoot的默认配置
    方式一:
    • 这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,我们要学会一种学习方式,通过源码探究,得出结论;这个结论一定是属于自己的,而且一通百通。

    • 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.

    我们要做的就是编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解;我们去自己写一个;我们新建一个包叫config,写一个类MyMvcConfig;

    package com.wlw.config;
    

    //应为类型要求为WebMvcConfigurer,所以我们实现其接口
    //可以使用自定义类扩展MVC的功能
    @Configuration //这个注解表明这是扩展的SpringMVC
    public class MyMvcConfig implements WebMvcConfigurer {

     <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">addViewControllers</span><span class="token punctuation">(</span>ViewControllerRegistry registry<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        registry<span class="token punctuation">.</span><span class="token function">addViewController</span><span class="token punctuation">(</span><span class="token string">"/wlw"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setViewName</span><span class="token punctuation">(</span><span class="token string">"index"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">//只需要访问 http://localhost:8080/wlw  就可以跳转到index.html</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 我们去浏览器访问一下:

    在这里插入图片描述

    • 确实也跳转过来了!所以说,我们要扩展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

    4、我们可以在这个类中去寻找一个我们刚才设置的viewController当做参考,发现它调用了一个

    protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }
    
     
     
    • 1
    • 2
    • 3

    5、我们点进去看一下

    public void addViewControllers(ViewControllerRegistry registry) {
        Iterator var2 = this.delegates.iterator();
    
    <span class="token keyword">while</span><span class="token punctuation">(</span>var2<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">// 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的</span>
        WebMvcConfigurer delegate <span class="token operator">=</span> <span class="token punctuation">(</span>WebMvcConfigurer<span class="token punctuation">)</span>var2<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        delegate<span class="token punctuation">.</span><span class="token function">addViewControllers</span><span class="token punctuation">(</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    8.4.5全面接管SpringMVC
    • 官方文档:
    If you want to take complete control of Spring MVCyou can add your own @Configuration annotated with @EnableWebMvc.
    
     
     
    • 1
    • 全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!

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

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

    • 不加注解之前,访问首页:

    在这里插入图片描述

    • 给配置类加上注解:@EnableWebMvc

    在这里插入图片描述

    • 我们发现所有的SpringMVC自动配置都失效了!回归到了最初的样子;

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

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

    1、这里发现它是导入了一个类:DelegatingWebMvcConfiguration (从容器中获取所有的webmvcconfig),我们可以继续进去看 (ctrl+左键点击这个注解@EnableWebMvc)

    @Import({DelegatingWebMvcConfiguration.class})
    public @interface EnableWebMvc {
    }
    
     
     
    • 1
    • 2
    • 3

    2、它继承了一个父类 WebMvcConfigurationSupport

    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
      // ......
    }
    
     
     
    • 1
    • 2
    • 3

    3、我们来回顾一下Webmvc自动配置类 (WebMvcAutoConfiguration 第五行代码)

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    // 这个注解的意思就是:容器中没有这个组件的时候,这个自动配置类才生效
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
        ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 总结一句话:@EnableWebMvc将WebMvcConfigurationSupport组件导入进来了;而导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能!,这个WebMvcConfigurationSupport进来之后,就使WebMvcAutoConfiguration这个类不满足条件,导致自动配置类失效!

    在SpringBoot中会有非常多的扩展配置,只要看见了这个,我们就应该多留心注意~

    九、一个简单的员工管理系统

    • 这个系统不是完整的,它只是为了看一下,使用SpringBoot开发,我们要进行哪些操作!

    9.1准备工作

    • 导入lombok 依赖

    • Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。用在实体类中,用注解生成构造方法,get/set,等方法

    • @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。

    • 导入lombok需要安装插件:可以参考这个文章:https://www.freesion.com/article/5093806462/

    •  			<!--lombok-->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值