视频链接:【三更草堂】SpringBoot2.5通俗易懂版

文章目录

  • SpringBoot-基础入门
    • 1. SpringBoot简介
      • 1.1 为什么要学习SpringBoot
      • 1.2 SpringBoot是什么
    • 2. 快速入门
      • 2.1 基本环境要求
        • Maven配置
        • 清理Maven仓库脚本
      • 2.2 HelloWorld
      • 2.3 常见问题及解决方案
      • 2.4 打包运行
      • 2.5 快速构建
    • 3.起步依赖
      • 3.0 依赖冲突及其解决方案
        • 3.0.1 依赖冲突
        • 3.0.2 解决方案
      • 3.1 版本锁定
      • 3.2 starter机制
    • 4.自动配置
    • 5.YML配置
      • 5.1.简介
        • 5.1.1YML是什么
        • 5.1.2YML优点
      • 5.2.语法
        • 5.2.1约定
        • 5.2.2键值关系
          • 普通值(字面量)
          • 日期
          • 对象(属性和值)、Map(键值对)
          • 数组、list、set
          • 对象数组、对象list、对象set
        • 5.2.3 占位符赋值
      • 5.3.SpringBoot读取YML
        • 5.3.1 @Value注解
        • 5.3.2 @ConfigurationProperties
      • 5.4.练习
        • 答案
      • 5.5 YML和properties配置的相互转换
      • 5.6 配置提示
  • SpringBoot-常见场景
    • 1.热部署
      • 1.1 准备工作
      • 1.2使用
    • 2.单元测试
      • 2.1 使用
        • ①添加依赖
        • ②编写测试类
      • 2.2 兼容老版本
    • 3.整合mybatis
      • 3.1 准备工作
      • 3.2 整合步骤
        • ①依赖
        • ②配置数据库信息
        • ③配置mybatis相关配置
        • ④编写Mapper接口
        • ⑤编写mapper接口对应的xml文件
        • ⑥测试
    • 4.Web开发
      • 4.1 静态资源访问
        • 4.1.1 修改静态资源访问路径
        • 4.1.2 修改静态资源存放目录
      • 4.2 设置请求映射规则@RequestMapping
        • 4.2.1 指定请求路径
        • 4.2.2 指定请求方式
        • 4.2.3 指定请求参数
        • 4.2.4 指定请求头
        • 4.2.5 指定请求头Content-Type
          • 范例一
          • 范例二
      • 4.3 获取请求参数
        • 4.3.1 获取路径参数
          • 范例一
          • 范例二
        • 4.3.2 获取请求体中的Json格式参数
          • 4.3.2.1 配置
          • 4.3.2.2 使用
            • 范例一
            • 范例二
          • 4.3.2.3 注意事项
        • 4.3.3 获取QueryString格式参数
          • 4.3.3.1 使用
            • 范例一
        • 4.3.4 相关注解其他属性
          • 4.3.4.1 required
          • 4.3.4.2 defaultValue
      • 4.4 响应体响应数据
        • 4.4.1 数据放到响应体
        • 4.4.2 数据转换成Json
          • 4.4.2.1 配置
          • 4.4.2.2 使用
        • 4.4.3 范例
          • 范例一
      • 4.5 跨域请求
        • 4.5.1 什么是跨域
        • 4.5.2 CORS解决跨域
        • 4.5.3 SpringBoot使用CORS解决跨域
          • 1.使用@CrossOrigin
          • 2.使用 WebMvcConfigurer 的 addCorsMappings 方法配置CorsInterceptor
      • 4.6 拦截器
        • 4.6.0 登录案例
          • 4.6.0.1 思路分析
          • 4.6.0.2 Token生成方案-JWT
          • 4.6.0.3 登录接口实现
          • 4.6.0.4 登录页面
        • 4.6.1 拦截器的概念
        • 4.6.1 使用步骤
          • ①创建类实现HandlerInterceptor接口
          • ②实现方法
          • ③配置拦截器
      • 4.7 异常统一处理
        • ①创建类加上@ControllerAdvice注解进行标识
        • ②定义异常处理方法
      • 4.8 获取web原生对象
      • 4.9 自定义参数解析
      • 4.10 声明式事务
      • 4.11 AOP
        • 4.11.1 使用步骤
        • 4.11.2 切换动态代理
      • 4.12 模板引擎相关-Thymeleaf
        • 4.12.1 快速入门
          • 4.12.1.1依赖
          • 4.12.1.2定义Controller
          • 4.12.1.3 htmL
    • 5.整合Redis
      • ①依赖
      • ②配置Redis地址和端口号
      • ③注入RedisTemplate使用
    • 6.环境切换
      • 6.1 为什么要使用profile
      • 6.2 使用
        • 6.2.1 创建profile配置文件
        • 6.2.2 激活环境
    • 7.日志
    • 8.指标监控
      • 8.1 使用
      • 8.2 常用端点
      • 8.3 图形化界面 SpringBoot Admin

SpringBoot-基础入门

1. SpringBoot简介

1.1 为什么要学习SpringBoot

​ 我们之前的SSM还是使用起来不够爽。

  • 还需要写很多的配置才能进行正常的使用。
  • 实现一个功能需要引入很多的依赖,尤其是要自己去维护依赖的版本,特别容易出现依赖冲突等问题。

​ SpringBoot就能很好的解决上述问题。

1.2 SpringBoot是什么

​ Spring Boot是基于Spring开发的全新框架,相当于对Spring做了又一层封装。

​ 其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。(自动配置)

​ 并且对第三方依赖的添加也进行了封装简化。(起步依赖)

​ 所以Spring能做的他都能做,并且简化了配置。

​ 并且还提供了一些Spring所没有的比如:

  • 内嵌web容器,不再需要部署到web容器中

    提供准备好的特性,如指标、健康检查和外部化配置

​ 最大特点:自动配置起步依赖

​ 官网:https://spring.io/projects/spring-boot

2. 快速入门

2.1 基本环境要求

​ JDK : 8

​ Maven :3.5.x

Maven配置

  <mirrors><mirror><id>aliyunmaven</id><mirrorOf>central</mirrorOf><name>aliyun maven</name><url>https://maven.aliyun.com/repository/public </url></mirror></mirrors>
  <profiles><profile><id>jdk-1.8</id><activation><activeByDefault>true</activeByDefault><jdk>1.8</jdk></activation><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion></properties></profile></profiles>

清理Maven仓库脚本

@echo off
rem create by NettQunrem 这里写你的仓库路径
set REPOSITORY_PATH=E:\Develop\maven_rep
rem 正在搜索...
for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (echo %%idel /s /q "%%i"
)
rem 搜索完毕
pause

创建一个bat文件,然后复制上述脚本进去,修改其中maven本地仓库的地址,保存后双击执行即可。

2.2 HelloWorld

①继承父工程

在pom.xml中添加一下配置,继承spring-boot-starter-parent这个父工程

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

②添加依赖

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

③创建启动类

创建一个类在其实加上@SpringBootApplication注解标识为启动类。

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

④定义Controller

创建Controller,主要Controller要放在启动类所在包或者其子包下。

//@Controller
//@ResponseBody
@RestController
public class HelloController {@RequestMapping("/hello")//@ResponseBodypublic String hello(){return "hello";}
}

⑤运行测试

直接运行启动类的main方法即可。

2.3 常见问题及解决方案

①访问时404

检查Controller是不是在启动类所在的包或者其子包下,如果不是需要进行修改。

②依赖爆红

配置阿里云镜像后刷新maven项目让其下载。

2.4 打包运行

​ 我们可以把springboot的项目打成jar包直接去运行。

①添加maven插件

    <build><plugins><!--springboot打包插件--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

②maven打包

③运行jar包

在jar包所在目录执行命令

java -jar jar包名称

即可运行。

2.5 快速构建

​ https://start.spring.io/

3.起步依赖

​ SpringBoot依靠父项目中的版本锁定和starter机制让我们能更轻松的实现对依赖的管理。

3.0 依赖冲突及其解决方案

3.0.1 依赖冲突

​ 一般程序在运行时发生类似于 java.lang.ClassNotFoundException,Method not found: ‘……’,或者莫名其妙的异常信息,这种情况一般很大可能就是 jar包依赖冲突的问题引起的了。

​ 一般在是A依赖C(低版本),B也依赖C(高版本)。 都是他们依赖的又是不同版本的C的时候会出现。

3.0.2 解决方案

​ 如果出现了类似于 java.lang.ClassNotFoundException,Method not found: 这些异常检查相关的依赖冲突问题,排除掉低版本的依赖,留下高版本的依赖。

3.1 版本锁定

​ 我们的SpringBoot模块都需要继承一个父工程:spring-boot-starter-parent。在spring-boot-starter-parent的父工程spring-boot-dependencies中对常用的依赖进行了版本锁定。这样我们在添加依赖时,很多时候都不需要添加依赖的版本号了。

​ 我们也可以采用覆盖properties配置或者直接指定版本号的方式修改依赖的版本。

例如:

直接指定版本号

        <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.7.2</version></dependency>

覆盖properties配置

    <properties><aspectj.version>1.7.2</aspectj.version></properties>

3.2 starter机制

​ 当我们需要使用某种功能时只需要引入对应的starter即可。一个starter针对一种特定的场景,其内部引入了该场景所需的依赖。这样我们就不需要单独引入多个依赖了。

​ 命名规律

  • 官方starter都是以 spring-boot-starter开头后面跟上场景名称。例如:spring-boot-starter-data-jpa
  • 非官方starter则是以 场景名-spring-boot-starter的格式,例如:mybatis-spring-boot-starter

4.自动配置

​ SpringBoot中最重要的特性就是自动配置。

​ Springboot遵循“约定优于配置”的原则,自动进行了默认配置。这样我们就不需要做大量的配置。

​ 当我们需要使用什么场景时,就会自动配置这个场景相关的配置。

​ 如果他的默认配置不符合我们的需求时修改这部分配置即可。

5.YML配置

5.1.简介

5.1.1YML是什么

​ YAML (YAML Ain’t a Markup Language)YAML不是一种标记语言,通常以.yml为后缀的文件,是一种直观的能够被电脑识别的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,一种专门用来写配置文件的语言。

​ YAML试图用一种比XML更敏捷的方式,来完成XML所完成的任务。

​ 例如:

student:name: sangengage: 15
<student><name>sangeng</name><age>15</age>
</student>

5.1.2YML优点

  1. YAML易于人们阅读。

  2. 更加简洁明了

5.2.语法

5.2.1约定

  • k: v 表示键值对关系,冒号后面必须有一个空格

  • 使用空格的缩进表示层级关系,空格数目不重要,只要是左对齐的一列数据,都是同一个层级的

  • 大小写敏感

  • 缩进时不允许使用Tab键,只允许使用空格

  • java中对于驼峰命名法,可用原名或使用-代替驼峰,如java中的lastName属性,在yml中使用lastName或 last-name都可正确映射。

  • yml中注释前面要加#

5.2.2键值关系

普通值(字面量)

k: v:字面量直接写;

字符串默认不用加上单引号或者双绰号;

“”: 双引号;转意字符能够起作用

​ name: “sangeng \n caotang”:输出;sangeng 换行 caotang

‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name1: sangeng
name2: 'sangeng  \n caotang'
name3: "sangeng  \n caotang"
age: 15
flag: true
日期
date: 2019/01/01
对象(属性和值)、Map(键值对)

多行写法:

在下一行来写对象的属性和值的关系,注意缩进

student:name: zhangsanage: 20

行内写法:

student: {name: zhangsan,age: 20}
数组、list、set

用- 值表示数组中的一个元素

多行写法:

pets:- dog- pig- cat

行内写法:

pets: [dog,pig,cat]
对象数组、对象list、对象set
students:- name: zhangsanage: 22- name: lisiage: 20- {name: wangwu,age: 18}

5.2.3 占位符赋值

可以使用 ${key:defaultValue} 的方式来赋值,若key不存在,则会使用defaultValue来赋值。

例如

server:port: ${myPort:88}myPort: 80

5.3.SpringBoot读取YML

5.3.1 @Value注解

​ 注意使用此注解只能获取简单类型的值(8种基本数据类型及其包装类,String,Date)

student:lastName: sangeng
@RestController
public class TestController {@Value("${student.lastName}")private String lastName;@RequestMapping("/test")public String test(){System.out.println(lastName);return "hi";}}

注意:加了@Value的类必须是交由Spring容器管理的

5.3.2 @ConfigurationProperties

​ yml配置

student:lastName: sangengage: 17
student2:lastName: sangeng2age: 15

​ 在类上添加注解@Component 和@ConfigurationProperties(prefix = “配置前缀”)

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "student")
public class Student {private String lastName;private Integer age;
}

​ 从spring容器中获取Student对象

@RestController
public class TestController {@Autowiredprivate Student student;@RequestMapping("/test")public String test(){System.out.println(student);return "hi";}
}

注意事项:要求对应的属性要有set/get方法,并且key要和成员变量名一致才可以对应的上。

lombok讲解视频:

https://www.bilibili.com/video/BV1G54y1V7VG?p=12

https://www.bilibili.com/video/BV1G54y1V7VG?p=13

5.4.练习

要求把下面实体类中的各个属性在yml文件中进行赋值。然后想办法读取yml配置的属性值,进行输出测试。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {private String lastName;private Integer age;private Boolean boss;private Date birthday;private Map<String,String> maps;private Map<String,String> maps2;private List<Dog> list;private Dog dog;private String[] arr;private String[] arr2;private Map<String,Dog> dogMap;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Dog {private String name;private Integer age;
}

答案

# 练习
student:lastName: sangengage: 15boss: truebirthday: 2006/2/3maps:name: sangengage: 11maps2: {name: caotang,age: 199}list:- name: 小白age: 3- name: 小黄age: 4- {name: 小黑,age: 1}dog:name: 小红age: 5arr:- sangeng- caotangarr2: [sangeng,caotang]dogMap:xb: {name: 小白,age: 9}xh:name: 小红age: 6

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "student")
public class Student {private String lastName;private Integer age;private Boolean boss;private Date birthday;private Map<String,String> maps;private Map<String,String> maps2;private List<Dog> list;private Dog dog;private String[] arr;private String[] arr2;private Map<String,Dog> dogMap;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Dog {private String name;private Integer age;
}

5.5 YML和properties配置的相互转换

​ 我们可以使用一些网站非常方便的实现YML和properties格式配置的相互转换。

转换网站:https://www.toyaml.com/index.html

5.6 配置提示

​ 如果使用了@ConfigurationProperties注解,可以增加以下依赖,让我们在书写配置时有相应的提示。

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

注意:添加完依赖加完注解后要运行一次程序才会有相应的提示。

SpringBoot-常见场景

1.热部署

​ SpringBoot为我们提供了一个方便我们开发测试的工具dev-tools。使用后可以实现热部署的效果。当我们运行了程序后对程序进行了修改,程序会自动重启。

​ 原理是使用了两个ClassLoder,一个ClassLoader加载哪些不会改变的类(第三方jar包),另一个ClassLoader加载会更改的类.称之为Restart ClassLoader,这样在有代码更改的时候,原来的Restart Classloader被丢弃,重新创建一个Restart ClassLoader,由于需要加载的类相比较少,所以实现了较快的重启。

1.1 准备工作

①设置IDEA自动编译

​ 在idea中的setting做下面配置

②设置允许程序运行时自动启动

​ ctrl + shift + alt + / 这组快捷键后会有一个小弹窗,点击Registry 就会进入下面的界面,找到下面的配置项并勾选,勾选后直接点close

1.2使用

①添加依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency>

②触发热部署

​ 当我们在修改完代码或者静态资源后可以切换到其它软件,让IDEA自动进行编译,自动编译后就会触发热部署。

​ 或者使用Ctrl+F9手动触发重新编译。

2.单元测试

​ 我们可以使用SpringBoot整合Junit进行单元测试。

Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库

​ Junit5功能相比于Junit4也会更强大。主要针对SpringBoot如何整合Junit进行单元测试做讲解。暂不针对Junit5的新功能做介绍。如有需要请看Junit5专门的课程。

2.1 使用

①添加依赖

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

②编写测试类

import com.sangeng.controller.HelloController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class ApplicationTest {wiredprivate HelloController helloController;@Testpublic void testJunit(){System.out.println(1);System.out.println(helloController);}
}

注意:测试类所在的包需要和启动类是在同一个包下。否则就要使用如下写法指定启动类。

import com.sangeng.controller.HelloController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;//classes属性来指定启动类
@SpringBootTest(classes = HelloApplication.class)
public class ApplicationTest {@Autowiredprivate HelloController helloController;@Testpublic void testJunit(){System.out.println(1);System.out.println(helloController);}
}

2.2 兼容老版本

​ 如果是对老项目中的SpringBoot进行了版本升级会发现之前的单元测试代码出现了一些问题。

​ 因为Junit5和之前的Junit4有比较大的不同。

​ 先看一张图:

​ 从上图可以看出 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform: 这是Junit提供的平台功能模块,通过它,其它的测试引擎也可以接入
  • JUnit JUpiter:这是JUnit5的核心,是一个基于JUnit Platform的引擎实现,它包含许多丰富的新特性来使得自动化测试更加方便和强大。
  • JUnit Vintage:这个模块是兼容JUnit3、JUnit4版本的测试引擎,使得旧版本的自动化测试也可以在JUnit5下正常运行。

​ 虽然Junit5包含了JUnit Vintage来兼容JUnit3和Junit4,但是SpringBoot 2.4 以上版本对应的spring-boot-starter-test移除了默认对 **Vintage 的依赖。**所以当我们仅仅依赖spring-boot-starter-test时会发现之前我们使用的@Test注解和@RunWith注解都不能使用了。

​ 我们可以单独在依赖vintage来进行兼容。

        <dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><scope>test</scope></dependency>

注意:

org.junit.Test对应的是Junit4的版本,就搭配@RunWith注解来使用。

SpringBoot2.2.0之前版本的写法

import com.sangeng.controller.HelloController;
//import org.junit.jupiter.api.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;//classes属性来指定启动类
@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationTest {@Autowiredprivate HelloController helloController;@Testpublic void testJunit(){System.out.println(1);System.out.println(helloController);}
}

3.整合mybatis

3.1 准备工作

①数据准备

/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(50) DEFAULT NULL,`age` int(11) DEFAULT NULL,`address` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;/*Data for the table `user` */insert  into `user`(`id`,`username`,`age`,`address`) values (2,'pdd',25,'上海'),(3,'UZI',19,'上海11'),(4,'RF',19,NULL),(6,'三更',14,'请问2'),(8,'test1',11,'cc'),(9,'test2',12,'cc2');/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

②实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private Integer id;private String username;private Integer age;private String address;
}

3.2 整合步骤

​ github: https://github.com/mybatis/spring-boot-starter/

①依赖

        <!--mybatis启动器--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>

②配置数据库信息

spring:datasource:url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver

③配置mybatis相关配置

mybatis:mapper-locations: classpath:mapper/*Mapper.xml # mapper映射文件路径type-aliases-package: com.sangeng.domain   # 配置哪个包下的类有默认的别名

④编写Mapper接口

注意在接口上加上@Mapper 和@Repository 注解

@Repository
@Mapper
public interface UserMapper {public List<User> findAll();
}

⑤编写mapper接口对应的xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.UserMapper"><select id="findAll" resultType="com.sangeng.domain.User">select * from user</select>
</mapper>

⑥测试

@SpringBootTest(classes = HelloApplication.class)
public class SpringMyTest {@AutowiredUserMapper userMapper;@Testpublic void tesMapper(){System.out.println(userMapper.findAll());}
}

4.Web开发

4.1 静态资源访问

​ 由于SpringBoot的项目是打成jar包的所以没有之前web项目的那些web资源目录(webapps)。

​ 那么我们的静态资源要放到哪里呢?

​ 从SpringBoot官方文档中我们可以知道,我们可以把静态资源放到 resources/static (或者 resources/public 或者resources/resources 或者 resources/META-INF/resources) 中即可。

​ 静态资源放完后,

​ 例如我们想访问文件:resources/static/index.html 只需要在访问时资源路径写成/index.html即可。

​ 例如我们想访问文件:resources/static/pages/login.html 访问的资源路径写成: /pages/login.html

4.1.1 修改静态资源访问路径

​ SpringBoot默认的静态资源路径匹配为/** 。如果想要修改可以通过 spring.mvc.static-path-pattern 这个配置进行修改。

​ 例如想让访问静态资源的url必须前缀有/res。例如/res/index.html 才能访问到static目录中的。我们可以修改如下:

在application.yml中

spring:mvc:static-path-pattern: /res/** #修改静态资源访问路径

4.1.2 修改静态资源存放目录

​ 我们可以修改 spring.web.resources.static-locations 这个配置来修改静态资源的存放目录。

​ 例如:

spring:web:resources:static-locations:- classpath:/sgstatic/ - classpath:/static/

4.2 设置请求映射规则@RequestMapping

​ 详细讲解:https://www.bilibili.com/video/BV1AK4y1o74Y P5-P12

​ 该注解可以加到方法上或者是类上。(查看其源码可知)

​ 我们可以用其来设定所能匹配请求的要求。只有符合了设置的要求,请求才能被加了该注解的方法或类处理。

4.2.1 指定请求路径

​ path或者value属性都可以用来指定请求路径。

例如:

​ 我们期望让请求的资源路径为**/test/testPath的请求能够被testPath**方法处理则可以写如下代码

@RestController
@RequestMapping("/test")
public class HelloController {@RequestMapping("/testPath")public String testPath(){return "testPath";}
}
@RestController
public class HelloController {@RequestMapping("/test/testPath")public String testPath(){return "testPath";}
}

4.2.2 指定请求方式

​ method属性可以用来指定可处理的请求方式。

例如:

​ 我们期望让请求的资源路径为**/test/testMethodPOST请求能够被testMethod**方法处理。则可以写如下代码

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testMethod",method = RequestMethod.POST)public String testMethod(){System.out.println("testMethod处理了请求");return "testMethod";}
}

注意:我们可以也可以运用如下注解来进行替换

  • ​ @PostMapping 等价于 @RequestMapping(method = RequestMethod.POST)

  • ​ @GetMapping 等价于 @RequestMapping(method = RequestMethod.GET)

  • ​ @PutMapping 等价于 @RequestMapping(method = RequestMethod.PUT)

  • ​ @DeleteMapping 等价于 @RequestMapping(method = RequestMethod.DELETE)

例如:

​ 上面的需求我们可以使用下面的写法实现

@RestController
@RequestMapping("/test")
public class TestController {@PostMapping(value = "/testMethod")public String testMethod(){System.out.println("testMethod处理了请求");return "testMethod";}
}

4.2.3 指定请求参数

​ 我们可以使用params属性来对请求参数进行一些限制。可以要求必须具有某些参数,或者是某些参数必须是某个值,或者是某些参数必须不是某个值。

例如:

​ 我们期望让请求的资源路径为**/test/testParamsGET请求,并且请求参数中具有code参数**的请求能够被testParams方法处理。则可以写如下代码

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testParams",method = RequestMethod.GET,params = "code")public String testParams(){System.out.println("testParams处理了请求");return "testParams";}
}

​ 如果是要求不能有code这个参数可以把改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testParams",method = RequestMethod.GET,params = "!code")public String testParams(){System.out.println("testParams处理了请求");return "testParams";}
}

​ 如果要求有code这参数,并且这参数值必须是某个值可以改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testParams",method = RequestMethod.GET,params = "code=sgct")public String testParams(){System.out.println("testParams处理了请求");return "testParams";}
}

​ 如果要求有code这参数,并且这参数值必须不是某个值可以改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testParams",method = RequestMethod.GET,params = "code!=sgct")public String testParams(){System.out.println("testParams处理了请求");return "testParams";}
}

4.2.4 指定请求头

​ 我们可以使用headers属性来对请求头进行一些限制。

例如:

​ 我们期望让请求的资源路径为**/test/testHeaders的GET请求,并且请求头中具有deviceType**的请求能够被testHeaders方法处理。则可以写如下代码

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testHeaders",method = RequestMethod.GET,headers = "deviceType")public String testHeaders(){System.out.println("testHeaders处理了请求");return "testHeaders";}
}

​ 如果是要求不能有deviceType这个请求头可以把改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testHeaders",method = RequestMethod.GET,headers = "!deviceType")public String testHeaders(){System.out.println("testHeaders处理了请求");return "testHeaders";}
}

​ 如果要求有deviceType这个请求头,并且其值必须是某个值可以改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testHeaders",method = RequestMethod.GET,headers = "deviceType=ios")public String testHeaders(){System.out.println("testHeaders处理了请求");return "testHeaders";}
}

​ 如果要求有deviceType这个请求头,并且其值必须不是某个值可以改成如下形式

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping(value = "/testHeaders",method = RequestMethod.GET,headers = "deviceType!=ios")public String testHeaders(){System.out.println("testHeaders处理了请求");return "testHeaders";}
}

4.2.5 指定请求头Content-Type

​ 我们可以使用consumes属性来对Content-Type这个请求头进行一些限制。

范例一

​ 我们期望让请求的资源路径为**/test/testConsumes**的POST请求,并且请求头中的Content-Type头必须为 multipart/from-data 的请求能够被testConsumes方法处理。则可以写如下代码

    @RequestMapping(value = "/testConsumes",method = RequestMethod.POST,consumes = "multipart/from-data")public String testConsumes(){System.out.println("testConsumes处理了请求");return "testConsumes";}
范例二

​ 如果我们要求请求头Content-Type的值必须不能为某个multipart/from-data则可以改成如下形式:

    @RequestMapping(value = "/testConsumes",method = RequestMethod.POST,consumes = "!multipart/from-data")public String testConsumes(){System.out.println("testConsumes处理了请求");return "testConsumes";}

4.3 获取请求参数

4.3.1 获取路径参数

​ RestFul风格的接口一些参数是在请求路径上的。类似: /user/1 这里的1就是id。

​ 如果我们想获取这种格式的数据可以使用**@PathVariable**来实现。

范例一

​ 要求定义个RestFul风格的接口,该接口可以用来根据id查询用户。请求路径要求为 /user ,请求方式要求为GET。

​ 而请求参数id要写在请求路径上,例如 /user/1 这里的1就是id。

​ 我们可以定义如下方法,通过如下方式来获取路径参数:

@RestController
public class UserController {@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)public String findUserById( @PathVariable("id")Integer id){System.out.println("findUserById");System.out.println(id);return "findUserById";}
}
范例二

​ 如果这个接口,想根据id和username查询用户。请求路径要求为 /user ,请求方式要求为GET。

​ 而请求参数id和name要写在请求路径上,例如 /user/1/zs 这里的1就是id,zs是name

​ 我们可以定义如下方法,通过如下方式来获取路径参数:

@RestController
public class UserController {@RequestMapping(value = "/user/{id}/{name}",method = RequestMethod.GET)public String findUser(@PathVariable("id") Integer id,@PathVariable("name") String name){System.out.println("findUser");System.out.println(id);System.out.println(name);return "findUser";}
}

4.3.2 获取请求体中的Json格式参数

​ RestFul风格的接口一些比较复杂的参数会转换成Json通过请求体传递过来。这种时候我们可以使用**@RequestBody**注解获取请求体中的数据。

4.3.2.1 配置

​ SpringBoot的web启动器已经默认导入了jackson的依赖,不需要再额外导入依赖了。

4.3.2.2 使用
范例一

​ 要求定义个RestFul风格的接口,该接口可以用来新建用户。请求路径要求为 /user ,请求方式要求为POST。

用户数据会转换成json通过请求体传递。
​ 请求体数据

{"name":"三更","age":15}

1.获取参数封装成实体对象

​ 如果我们想把Json数据获取出来封装User对象,我们可以这样定义方法:

@RestController
public class UserController {@RequestMapping(value = "/user",method = RequestMethod.POST)public String insertUser(@RequestBody User user){System.out.println("insertUser");System.out.println(user);return "insertUser";}
}

​ User实体类如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String name;private Integer age;
}

2.获取参数封装成Map集合

​ 也可以把该数据获取出来封装成Map集合:

    @RequestMapping(value = "/user",method = RequestMethod.POST)public String insertUser(@RequestBody Map map){System.out.println("insertUser");System.out.println(map);return "insertUser";}
范例二

​ 如果请求体传递过来的数据是一个User集合转换成的json,Json数据可以这样定义:

[{"name":"三更1","age":14},{"name":"三更2","age":15},{"name":"三更3","age":16}]

​ 方法定义:

    @RequestMapping(value = "/users",method = RequestMethod.POST)public String insertUsers(@RequestBody List<User> users){System.out.println("insertUsers");System.out.println(users);return "insertUser";}
4.3.2.3 注意事项

​ 如果需要使用**@RequestBody**来获取请求体中Json并且进行转换,要求请求头 Content-Type 的值要为: application/json 。

4.3.3 获取QueryString格式参数

​ 如果接口的参数是使用QueryString的格式的话,我们也可以使用SpringMVC快速获取参数。

​ 我们可以使用**@RequestParam**来获取QueryString格式的参数。

4.3.3.1 使用
范例一

​ 要求定义个接口,该接口请求路径要求为 /testRequestParam,请求方式无要求。参数为id和name和likes。使用QueryString的格式传递。

1.参数单独的获取

​ 如果我们想把id,name,likes单独获取出来可以使用如下写法:

​ 在方法中定义方法参数,方法参数名要和请求参数名一致,这种情况下我们可以省略**@RequestParam**注解。

    @RequestMapping("/testRquestParam")public String testRquestParam(Integer id, String name, String[] likes){System.out.println("testRquestParam");System.out.println(id);System.out.println(name);System.out.println(Arrays.toString(likes));return "testRquestParam";}

​ 如果方法参数名和请求参数名不一致,我们可以加上**@RequestParam**注解例如:

    @RequestMapping("/testRquestParam")public String testRquestParam(@RequestParam("id") Integer uid,@RequestParam("name") String name, @RequestParam("likes")String[] likes){System.out.println("testRquestParam");System.out.println(uid);System.out.println(name);System.out.println(Arrays.toString(likes));return "testRquestParam";}

2.获取参数封装成实体对象

​ 如果我们想把这些参数封装到一个User对象中可以使用如下写法:

    @RequestMapping("/testRquestParam")public String testRquestParam(User user){System.out.println("testRquestParam");System.out.println(user);return "testRquestParam";}

​ User类定义如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String name;private Integer age;private String[] likes;
}

​ 测试时请求url如下:

http://localhost:8080/testRquestParam?id=1&name=三更草堂&likes=编程&likes=录课&likes=烫头

注意:实体类中的成员变量要和请求参数名对应上。并且要提供对应的set/get方法。

4.3.4 相关注解其他属性

4.3.4.1 required

​ 代表是否必须,默认值为true也就是必须要有对应的参数。如果没有就会报错。

​ 如果对应的参数可传可不传则可以把其设置为fasle

例如:

    @RequestMapping("/testRquestParam")public String testRquestParam(@RequestParam(value = "id",required = false) Integer uid,@RequestParam("name") String name, @RequestParam("likes")String[] likes){System.out.println("testRquestParam");System.out.println(uid);System.out.println(name);System.out.println(Arrays.toString(likes));return "testRquestParam";}
4.3.4.2 defaultValue

​ 如果对应的参数没有,我们可以用defaultValue属性设置默认值。

例如:

    @RequestMapping("/testRquestParam")public String testRquestParam(@RequestParam(value = "id",required = false,defaultValue = "777") Integer uid,@RequestParam("name") String name, @RequestParam("likes")String[] likes){System.out.println("testRquestParam");System.out.println(uid);System.out.println(name);System.out.println(Arrays.toString(likes));return "testRquestParam";}

4.4 响应体响应数据

​ 无论是RestFul风格还是我们之前web阶段接触过的异步请求,都需要把数据转换成Json放入响应体中。

4.4.1 数据放到响应体

​ 我们的SpringMVC为我们提供了**@ResponseBody**来非常方便的把Json放到响应体中。

@ResponseBody可以加在哪些东西上面?类上和方法上

​ 具体代码请参考范例。

4.4.2 数据转换成Json

4.4.2.1 配置

​ SpringBoot项目中使用了web的start后,不需要进行额外的依赖和配置

4.4.2.2 使用

​ 只要把要转换的数据直接作为方法的返回值返回即可。SpringMVC会帮我们把返回值转换成json。具体代码请参考范例。

4.4.3 范例

范例一

​ 要求定义个RestFul风格的接口,该接口可以用来根据id查询用户。请求路径要求为 /response/user ,请求方式要求为GET。

​ 而请求参数id要写在请求路径上,例如 /response/user/1 这里的1就是id。

​ 要求获取参数id,去查询对应id的用户信息(模拟查询即可,可以选择直接new一个User对象),并且转换成json响应到响应体中。

@Controller
@RequestMapping("/response")
public class ResponseController {@RequestMapping("/user/{id}")@ResponseBodypublic User findById(@PathVariable("id") Integer id){User user = new User(id, "三更草堂", 15, null);return user;}
}

4.5 跨域请求

4.5.1 什么是跨域

​ 浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。 同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

4.5.2 CORS解决跨域

​ CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

​ 它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。

4.5.3 SpringBoot使用CORS解决跨域

1.使用@CrossOrigin

可以在支持跨域的方法上或者是Controller上加上@CrossOrigin注解

@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {@Autowiredprivate UserServcie userServcie;@RequestMapping("/findAll")//@CrossOriginpublic ResponseResult findAll(){//调用service查询数据 ,进行返回List<User> users = userServcie.findAll();return new ResponseResult(200,users);}
}
2.使用 WebMvcConfigurer 的 addCorsMappings 方法配置CorsInterceptor
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**")// 设置允许跨域请求的域名.allowedOriginPatterns("*")// 是否允许cookie.allowCredentials(true)// 设置允许的请求方式.allowedMethods("GET", "POST", "DELETE", "PUT")// 设置允许的header属性.allowedHeaders("*")// 跨域允许时间.maxAge(3600);}
}

4.6 拦截器

4.6.0 登录案例

4.6.0.1 思路分析

​ 在前后端分离的场景中,很多时候会采用token的方案进行登录校验。

​ 登录成功时,后端会根据一些用户信息生成一个token字符串返回给前端。

​ 前端会存储这个token。以后前端发起请求时如果有token就会把token放在请求头中发送给后端。

​ 后端接口就可以获取请求头中的token信息进行解析,如果解析不成功说明token超时了或者不是正确的token,相当于是未登录状态。

​ 如果解析成功,说明前端是已经登录过的。

4.6.0.2 Token生成方案-JWT

​ 本案例采用目前企业中运用比较多的JWT来生成token。

​ 使用时先引入相关依赖

        <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>

​ 然后可以使用下面的工具类来生成和解析token

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;/*** JWT工具类*/
public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时//设置秘钥明文public static final String JWT_KEY = "sangeng";/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);SecretKey secretKey = generalKey();JwtBuilder builder = Jwts.builder().setId(id)              //唯一的ID.setSubject(subject)   // 主题  可以是JSON数据.setIssuer("sg")     // 签发者.setIssuedAt(now)      // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);// 设置过期时间return builder.compact();}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}
4.6.0.3 登录接口实现

数据准备

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(50) DEFAULT NULL,`password` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;/*Data for the table `sys_user` */insert  into `sys_user`(`id`,`username`,`password`) values (1,'root','root'),(2,'sangeng','caotang');

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SystemUser {private Integer id;private String username;private String password;
}

SystemUserController

import com.sangeng.domain.ResponseResult;
import com.sangeng.domain.SystemUser;
import com.sangeng.service.SystemUserService;
import com.sangeng.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@RestController
@RequestMapping("/sys_user")
public class SystemUserController {@Autowiredprivate SystemUserService userService;@PostMapping("/login")public ResponseResult login(@RequestBody SystemUser user) {//校验用户名密码是否正确SystemUser loginUser = userService.login(user);Map<String, Object> map;if (loginUser != null) {//如果正确 生成token返回map = new HashMap<>();String token = JwtUtil.createJWT(UUID.randomUUID().toString(), String.valueOf(loginUser.getId()), null);map.put("token", token);} else {//如果不正确 给出相应的提示return new ResponseResult(300, "用户名或密码错误,请重新登录");}return new ResponseResult(200, "登录成功", map);}
}

Service

public interface SystemUserService {public SystemUser login(SystemUser user);
}
@Service
public class SystemUserServcieImpl implements SystemUserService {@Autowiredprivate SystemUserMapper systemUserMapper;@Overridepublic SystemUser login(SystemUser user) {SystemUser loginUser = systemUserMapper.login(user);return loginUser;}
}

dao

@Mapper
@Repository
public interface UserMapper {List<User> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.SystemUserMapper"><select id="login" resultType="com.sangeng.domain.SystemUser">select * from sys_user where username = #{username} and password = #{password}</select>
</mapper>
4.6.0.4 登录页面

表单中 input组件加上

<input  ...v-model="user.username"....>
<input  ...v-model="user.password"....>

提交按钮 @click=“handleLogin()”

<button ....@click="handleLogin()"....>登录</button>

前端页面

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>var v = new Vue({el:"#app",data:{user:{}},methods:{handleLogin(){//请求后台登录接口axios.post("http://localhost/sys_user/login",this.user).then((res)=>{// console.log(res);//判断是否成功if(res.data.code==200){//登录成功// alert("登录成功");//存储tokenlocalStorage.setItem("token",res.data.data.token);//跳转页面到index.htmllocation.href = "/index.html";}else{//登录失败alert(res.data.msg);}})}}});
</script>

4.6.1 拦截器的概念

​ 如果我们想在多个Handler方法执行之前或者之后都进行一些处理,甚至某些情况下需要拦截掉,不让Handler方法执行。那么可以使用SpringMVC为我们提供的拦截器。

​ 详情见 https://space.bilibili.com/663528522 SpringMVC课程中拦截器相关章节。

4.6.1 使用步骤

①创建类实现HandlerInterceptor接口
public class LoginInterceptor implements HandlerInterceptor {}
②实现方法
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的tokenString token = request.getHeader("token");//判断token是否为空,如果为空也代表未登录 提醒重新登录(401)if(!StringUtils.hasText(token)){response.sendError(HttpServletResponse.SC_UNAUTHORIZED);return false;}//解析token看看是否成功try {Claims claims = JwtUtil.parseJWT(token);String subject = claims.getSubject();System.out.println(subject);} catch (Exception e) {e.printStackTrace();//如果解析过程中没有出现异常说明是登录状态//如果出现了异常,说明未登录,提醒重新登录(401)response.sendError(HttpServletResponse.SC_UNAUTHORIZED);return false;}return true;}
}
③配置拦截器
@Configuration
public class LoginConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor)//添加拦截器.addPathPatterns("/**")  //配置拦截路径.excludePathPatterns("/sys_user/login");//配置排除路径}
}

4.7 异常统一处理

①创建类加上@ControllerAdvice注解进行标识

@ControllerAdvice
public class MyControllerAdvice {}

②定义异常处理方法

​ 定义异常处理方法,使用**@ExceptionHandler**标识可以处理的异常。

@ControllerAdvice
public class MyControllerAdvice {@ExceptionHandler(RuntimeException.class)@ResponseBodypublic ResponseResult handlerException(Exception e){//获取异常信息,存放如ResponseResult的msg属性String message = e.getMessage();ResponseResult result = new ResponseResult(300,message);//把ResponseResult作为返回值返回,要求到时候转换成json存入响应体中return result;}
}

4.8 获取web原生对象

​ 我们之前在web阶段我们经常要使用到request对象,response,session对象等。我们也可以通过SpringMVC获取到这些对象。(不过在MVC中我们很少获取这些对象,因为有更简便的方式,避免了我们使用这些原生对象相对繁琐的API。)

​ 我们只需要在方法上添加对应类型的参数即可,但是注意数据类型不要写错了,SpringMVC会把我们需要的对象传给我们的形参。

@RestController
public class TestController {@RequestMapping("/getRequestAndResponse")public ResponseResult getRequestAndResponse(HttpServletRequest request, HttpServletResponse response, HttpSession session){System.out.println(request);return new ResponseResult(200,"成功");}
}

4.9 自定义参数解析

​ 如果我们想实现像获取请求体中的数据那样,在Handler方法的参数上增加一个@RepuestBody注解就可以获取到对应的数据的话。

​ 可以使用HandlerMethodArgumentResolver来实现自定义的参数解析。

①定义用来标识的注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUserId {}

②创建类实现HandlerMethodArgumentResolver接口并重写其中的方法

注意加上@Component注解注入Spring容器

public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {//判断方法参数使用能使用当前的参数解析器进行解析@Overridepublic boolean supportsParameter(MethodParameter parameter) {//如果方法参数有加上CurrentUserId注解,就能把被我们的解析器解析return parameter.hasParameterAnnotation(CurrentUserId.class);}//进行参数解析的方法,可以在方法中获取对应的数据,然后把数据作为返回值返回。方法的返回值就会赋值给对应的方法参数@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {//获取请求头中的tokenString token = webRequest.getHeader("token");if(StringUtils.hasText(token)){//解析token,获取userIdClaims claims = JwtUtil.parseJWT(token);String userId = claims.getSubject();//返回结果return userId;}return null;}
}

③配置参数解析器

@Configuration
public class ArgumentResolverConfig implements WebMvcConfigurer {@Autowiredprivate UserIdArgumentResolver userIdArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userIdArgumentResolver);}
}

④测试

在需要获取UserId的方法中增加对应的方法参数然后使用@CurrentUserId进行标识即可获取到数据

@RestController
@RequestMapping("/user")
//@CrossOrigin
public class UserController {@Autowiredprivate UserServcie userServcie;@RequestMapping("/findAll")public ResponseResult findAll(@CurrentUserId String userId) throws Exception {System.out.println(userId);//调用service查询数据 ,进行返回sList<User> users = userServcie.findAll();return new ResponseResult(200,users);}
}

4.10 声明式事务

​ 直接在需要事务控制的方法上加上对应的注解**@Transactional**

@Service
public class UserServiceImpl implements UserServcie {@Autowiredprivate UserMapper userMapper;@Overridepublic List<User> findAll() {return userMapper.findAll();}@Override@Transactionalpublic void insertUser() {//添加2个用户到数据库User user = new User(null,"sg666",15,"上海");User user2 = new User(null,"sg777",16,"北京");userMapper.insertUser(user);System.out.println(1/0);userMapper.insertUser(user2);}
}

4.11 AOP

​ AOP详细知识学习见:https://space.bilibili.com/663528522 中的Spring教程

​ 在SpringBoot中默认是开启AOP功能的。如果不想开启AOP功能可以使用如下配置设置为false

spring:aop:auto: false

4.11.1 使用步骤

①添加依赖

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

②自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokeLog {}

③定义切面类

@Aspect  //标识这是一个切面类
@Component
public class InvokeLogAspect {//确定切点@Pointcut("@annotation(com.sangeng.aop.InvokeLog)")public void pt(){}@Around("pt()")public Object printInvokeLog(ProceedingJoinPoint joinPoint){//目标方法调用前Object proceed = null;MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();System.out.println(methodName+"即将被调用");try {proceed = joinPoint.proceed();//目标方法调用后System.out.println(methodName+"被调用完了");} catch (Throwable throwable) {throwable.printStackTrace();//目标方法出现异常了System.out.println(methodName+"出现了异常");}return proceed;}
}

④在需要正确的地方增加对应的注解

@Service
public class UserServiceImpl implements UserServcie {@Autowiredprivate UserMapper userMapper;@Override@InvokeLog  //需要被增强方法需要加上对应的注解public List<User> findAll() {return userMapper.findAll();}
}

4.11.2 切换动态代理

​ 有的时候我们需要修改AOP的代理方式。

​ 我们可以使用以下方式修改:

​ 在配置文件中配置spring.aop.proxy-target-class为false这为使用jdk动态代理。该配置默认值为true,代表使用cglib动态代理。

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = false)//修改代理方式
public class WebApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(WebApplication.class, args);}
}

​ 如果想生效还需要在配置文件中做如下配置

spring:aop:proxy-target-class: false #切换动态代理的方式

4.12 模板引擎相关-Thymeleaf

4.12.1 快速入门

4.12.1.1依赖
        <!--thymeleaf依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
4.12.1.2定义Controller

在controller中往域中存数据,并且跳转

@Controller
public class ThymeleafController {@Autowiredprivate UserServcie userServcie;@RequestMapping("/thymeleaf/users")public String users(Model model){//获取数据List<User> users = userServcie.findAll();//望域中存入数据model.addAttribute("users",users);model.addAttribute("msg","hello thymeleaf");//页面跳转return "table-standard";}
}
4.12.1.3 htmL

resources\templates下存放模板页面。

在html标签中加上 xmlns:th=“http://www.thymeleaf.org”

获取域中的name属性的值可以使用: ${name} 注意要在th开头的属性中使用

<html lang="en" class="no-ie" xmlns:th="http://www.thymeleaf.org">.....<div class="panel-heading" th:text="${msg}">Kitchen Sink</div>

如果需要引入静态资源,需要使用如下写法。

   <link rel="stylesheet" th:href="@{/app/css/bootstrap.css}"><!-- Vendor CSS--><link rel="stylesheet" th:href="@{/vendor/fontawesome/css/font-awesome.min.css}"><link rel="stylesheet" th:href="@{/vendor/animo/animate+animo.css}"><link rel="stylesheet" th:href="@{/vendor/csspinner/csspinner.min.css}"><!-- START Page Custom CSS--><!-- END Page Custom CSS--><!-- App CSS--><link rel="stylesheet" th:href="@{/app/css/app.css}"><!-- Modernizr JS Script--><script th:src="@{/vendor/modernizr/modernizr.js}" type="application/javascript"></script><!-- FastClick for mobiles--><script th:src="@{/vendor/fastclick/fastclick.js}" type="application/javascript"></script>

遍历语法:遍历的语法 th:each=“自定义的元素变量名称 : ${集合变量名称}”

<tr th:each="user:${users}"><td th:text="${user.id}"></td><td th:text="${user.username}"></td><td th:text="${user.age}"></td><td th:text="${user.address}"></td>
</tr>

5.整合Redis

①依赖

        <!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

②配置Redis地址和端口号

spring:redis:host: 127.0.0.1 #redis服务器ip地址port: 6379  #redis端口号

③注入RedisTemplate使用

    @Autowiredprivate StringRedisTemplate redisTemplate;@Testpublic void testRedis(){redisTemplate.opsForValue().set("name","三更");}

6.环境切换

6.1 为什么要使用profile

​ 在实际开发环境中,我们存在开发环境的配置,部署环境的配置,测试环境的配置等等,里面的配置信息很多时,例如:端口、上下文路径、数据库配置等等,若每次切换环境时,我们都需要进行修改这些配置信息时,会比较麻烦,profile的出现就是为了解决这个问题。它可以让我们针对不同的环境进行不同的配置,然后可以通过激活、指定参数等方式快速切换环境。

6.2 使用

6.2.1 创建profile配置文件

​ 我们可以用application-xxx.yml的命名方式 创建配置文件,其中xxx可以根据自己的需求来定义。

​ 例如

​ 我们需要一个测试环境的配置文件,则可以命名为:application-test.yml

​ 需要一个生产环境的配置文件,可以命名为:application-prod.yml

​ 我们可以不同环境下不同的配置放到对应的profile文件中进行配置。然后把不同环境下都相同的配置放到application.yml文件中配置。

6.2.2 激活环境

​ 我们可以再application.yml文件中使用spring.profiles.active属性来配置激活哪个环境。

​ 也可以使用虚拟机参数来指定激活环境。例如 : -Dspring.profiles.active=test

​ 也可以使用命令行参数来激活环境。例如: –spring.profiles.active =test

7.日志

​ 开启日志

debug: true #开启日志
logging:level:com.sangeng: debug #设置日志级别

8.指标监控

​ 我们在日常开发中需要对程序内部的运行情况进行监控, 比如:健康度、运行指标、日志信息、线程状况等等 。而SpringBoot的监控Actuator就可以帮我们解决这些问题。

8.1 使用

①添加依赖

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

②访问监控接口

http://localhost:81/actuator

③配置启用监控端点

management:endpoints:enabled-by-default: true #配置启用所有端点web:exposure:include: "*" #web端暴露所有端点

8.2 常用端点

端点名称 描述
beans 显示应用程序中所有Spring Bean的完整列表。
health 显示应用程序运行状况信息。
info 显示应用程序信息。
loggers 显示和修改应用程序中日志的配置。
metrics 显示当前应用程序的“指标”信息。
mappings 显示所有@RequestMapping路径列表。
scheduledtasks 显示应用程序中的计划任务。

8.3 图形化界面 SpringBoot Admin

①创建SpringBoot Admin Server应用

要求引入spring-boot-admin-starter-server依赖

        <dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-server</artifactId></dependency>

然后在启动类上加上@EnableAdminServer注解

②配置SpringBoot Admin client应用

在需要监控的应用中加上spring-boot-admin-starter-client依赖

        <dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-client</artifactId><version>2.3.1</version></dependency>

然后配置SpringBoot Admin Server的地址

spring:boot:admin:client:url: http://localhost:8888 #配置 Admin Server的地址

SpringBoot笔记通俗易懂版相关推荐

  1. Spring笔记通俗易懂版

    视频链接:[三更草堂]Spring5框架教程通俗易懂版 文章目录 Spring-01使用IOC 1.Spring简介 2.IOC控制反转 2.1 概念 2.2 入门案例 ①导入依赖 ②编写配置文件 ③ ...

  2. R语言基础入门(学习笔记通俗易懂版)

    文章目录 R语言预备知识 获取工作目录 设置工作目录 注释 变量名的命名 赋值 变量的显示 查看与清除变量 函数帮助文档查询 函数 安装R包 文件的读取 文件的输出 软件的退出与保存 R语言语法 向量 ...

  3. SpringBoot笔记:SpringBoot2.3集成SpringSession+nginx+redis实现session共享

    文章目录 Spring Session介绍 Redis集成 yml配置 依赖添加 redis存值查看 登录服务器查看redis的值 查询所有"spring:session:"开头的 ...

  4. Deeplearning.ai深度学习课程笔记-在线版

    注意:请点击阅读原文 课程概述 课程视频离线版本可以到github:https://github.com/fengdu78/deeplearning_ai_books 查找下载. 课程地址:https ...

  5. 算法笔记(JavaScript版)——排序

    算法笔记(JavaScript版)--排序 本文内容根据Rebert Sedgewick和Kevin Wayne的<算法(第四版)>整理,原代码为java语言,自己修改为JavaScrip ...

  6. SpringBoot笔记整理(四)

    SpringBoot笔记整理(一) SpringBoot笔记整理(二) SpringBoot笔记整理(三) SpringBoot笔记整理(四) 1.SpringMVC自动配置 以下是SpringBoo ...

  7. SpringBoot笔记整理(三)

    SpringBoot笔记整理(一) SpringBoot笔记整理(二) SpringBoot笔记整理(三) SpringBoot笔记整理(四) Web开发 1.使用SpringBoot: 1)创建Sp ...

  8. SpringBoot笔记整理(二)

    SpringBoot笔记整理(一) SpringBoot笔记整理(二) SpringBoot笔记整理(三) SpringBoot笔记整理(四) Spring Boot与日志(日志框架.日志配置) 1. ...

  9. SpringBoot笔记整理(一)

    SpringBoot笔记整理(一) SpringBoot笔记整理(二) SpringBoot笔记整理(三) SpringBoot笔记整理(四) 1.使用Spring Initializer快速创建Sp ...

最新文章

  1. golang中的shell
  2. 人工神经网络_用人工神经网络控制猴子大脑,MIT科学家做到了
  3. opencv-python教程学习系列13-图像平滑
  4. 云计算将会让数据中心消失?
  5. Facebook黄毅博士:像加工艺术品一样构建技术产品
  6. abb伺服电机如何进行挑选_PLC是怎么控制伺服电机的?如何设计一个伺服系统?...
  7. 【数学建模】CUMCM-2016A 系泊系统的设计 解题思路整理
  8. 希尔伯特变换产生负频率解决方法
  9. oracle虚拟机内鼠标消失,鼠标消失在VirtualBox中
  10. 论文-《Conversational Recommender System》
  11. Python——爬取单章小说内容
  12. 最好的 6 个免费天气 API 接口对比测评
  13. VMware下安装虚拟机windows server 2016
  14. 云服务系列文章(一) 阿里云和AWS
  15. Linux目录配置与FHS标准
  16. Android 音视频开发实践系列-04-Android WebRTC推流到SRS服务器实现直播功能
  17. 谷歌浏览器为什么推荐使用搜狗搜索?知乎网友的脑回路真是……
  18. 为什么Android手机APP闪退,安卓手机软件闪退怎么办 安卓软件停止运行解决方法_系统圣地...
  19. 使用Canvas画布实现简单的计时器功能
  20. 简单工厂模式---女娲造人

热门文章

  1. 获取微信小程序源码教程
  2. 为何别人家的会议井然有序?原来是它的功劳
  3. K-Means算法及其变种,优缺点分析笔记
  4. 【软件测试】白盒测试用例设计——基本路径法
  5. 仿猫眼官网静态页面(纯HTML)
  6. 计算机培训教案入门,计算机二级学习基础PPT学习教案.pptx
  7. 五家共井 穷举法_第5讲地图着色问题.ppt
  8. 关于题目15820831的答案问题
  9. c语言中多线程的执行顺序,ReentrantLock实现 多线程顺序执行任务
  10. Python turtle 画正多边形和多角形