SpringBoot的基本使用

为什么要学习SpringBoot

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

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

SpringBoot是什么

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

其设计的目的是用来简化Spring的初始搭建开发过程,该框架使用了特定的方式进行配置,从而使开发人员不需要定义样板化的配置;并且对第三方依赖的添加也进行了封装简化

Spring能做的他都能做,并且简化了配置,并且提供了Spring没有的:

  • 内嵌WEB容器,不需要再部署到web容器中
  • 提供准备好的特性,如指标、健康检查和外部化配置

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

快速入门

基本环境要求

JDK8;Maven3.5

1
2
3
4
5
6
7
8
9
10
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>hhttps://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>

<localRepository>D:/apache-maven-3.6.3/m2/repository</localRepository>

HelloWorld

配置父工程

1
2
3
4
5
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
</parent>

添加依赖

1
2
3
4
5
6
7
8
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

</dependencies>

创建启动类

@SpringBootApplication注解标识为启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.xiaohupao;

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

/**
* @Author: xiaohupao
* @Date: 2021/7/10 21:19
*/
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}

定义Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @Author: xiaohupao
* @Date: 2021/7/10 21:27
*/
/*@Controller
@ResponseBody*/
@RestController // 相当于@Controller + @ResponseBody
public class HelloController {
@GetMapping(value = "/hello")
public String hello(){
return "HelloSpringBoot";
}
}

运行测试是直接运行启动类的main方法

常见问题及解决方案

访问时404

将Controller放在启动类所在的包及其子包下

打包部署

可以把SpringBoot的项目打成jar包直接运行

添加maven插件

1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

maven打包

package

运行jar包

在jar包所在的目录执行命令

1
java -jar jar包名称

快速构建

起步依赖

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

依赖冲突及其解决方案

依赖冲突

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

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

解决方案

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

安装Maven Helper插件

版本锁定

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

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

starter机制

一个starter针对一种特定的场景,其内部引入了该场景所需的依赖。这样我们就不需要单独引入多个依赖了。

官方starter都是以spring-boot-starter开头后面跟上场景名称
非官方starter都是以场景名-spring-boot-starter的格式

自动配置

SpringBoot中最重要的特性就是自动配置
SpringBoot遵循约定优于配置的原则,自动进行了默认配置。这样我们就不需要做大量的配置。
当我们需要使用什么场景时,就会自动配置这个场景相关的配置
如果他的默认配置不符合我们的需求时修改这部分配置即可

YML配置

YML简介

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

YAML试图一种比XML更简洁的方式,来完成XML所完成的任务。

YML优点

  • YAML易于人们阅读
  • 更加简洁明了

YML语法

约定

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

    • 字符串默认不用加上单引号或双绰号;双引号,转义符能够起作用;单引号会转义特殊字符,特殊字符最终只是一个普通的字符串数据

    • ```yaml
      name1: wk
      name2: lsn
      age: 18
      flag: true

      1
      2
      3
      4
      5

      * 日期

      * ```yaml
      date: 2021/10/25
  • 使用空格的缩进表示层级关系,空格数目不重要,只要是左对齐的一列数据,都是同一层的

  • 大小写敏感

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

  • java中对于驼峰命名,可用原名或使用-代替驼峰,例如:lastName;last-name可完成映射

对象、Map

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

1
2
3
student:
name: lsn
age: 18
1
2
3
map:
k1: v1
k2: v2

行内写法:

1
student: {name: lsn, age: 20}

数组、list、set

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

多行写法:

1
2
3
4
pets:
- dog
- pig
- cat

行内写法:

1
pets: [dog, pig, cat]

对象数组、对象list、对象set

1
2
3
4
5
students:
- name: wk
age: 25
- name: lsn
age: 23

占位符赋值

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

1
2
3
4
server:
port: ${myPort}

myPort: 80
1
2
3
4
server:
port: ${myPort: 88}

myPort: 80

SpringBoot读取YML

@Value注解

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

1
2
student:
lastName: lsn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {

@Value("${student.lastName}")
private String lastName;

@RequestMapping(value = "/test")
public String test(){
System.out.println(lastName);
return "hi";
}
}

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

@ConfigurationProperties

1
2
3
student:
name: lsn
age: 18

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.xiaohupao.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "student")
public class Student {

private String name;

private Integer age;
}

注意,成员变量一定要提供setget方法

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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {

@Value("${student.name}")
private String lastName;

@RequestMapping(value = "/test")
public String test(){
System.out.println(lastName);
System.out.println(student);
return "hi";
}


@Autowired
private Student student;

}
/**
lsn
Student(name=lsn, age=18)
*/

YML练习

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
student:
lastName: lsn
age: 23
boss: true
birthday: 1998/10/25
maps:
name: lsn
age: 12
maps2: {name: nsl, age: 22}
lists:
- name: xiaobai
age: 3
- name: xiaohuang
age: 4
- {name: xiaohei,age: 4}
dog:
name: xiaohong
age: 5
arr:
- wk
- lsn
arr2: [wk, lsn]
dogMap:
xb: {name: xiaobai, age: 3}
xh: {name: xiaohuang, age: 4}
xhong: {name: xiaohong, age: 5}
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
package cn.xiaohupao.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

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

/**
* @author xiaohupao
* @date 2021/7/12
*/
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
@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> lists;

private Dog dog;
private String[] arr;
private String[] arr2;

private Map<String, Dog> dogMap;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.xiaohupao.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
private String name;
private Integer age;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@RestController
public class StudentController {

@Autowired
private Student student;

@GetMapping(value = "/studentTest")
public String studentTest(){
System.out.println(student);
return "hi";
}
}

YML和properties配置的相互转换

转换网站:yaml转properties-properties转yaml

配置提示问题

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

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

SpringBoot-常见场景

热部署

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

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

准备工作

设置IDEA自动编译:compiler -> Build project automatically

允许程序运行时自动启动:ctrl + alt+ shift+ / 选择Registry 找到compiler.automake.allow.when.app.running打勾

使用

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

触发热部署:

在修改完代码或静态资源时,可以切换到其他软件,让IDEA自动进行编译,自动编译后就会触发热部署。或者使用ctrl + F9触发重新编译

单元测试

可以使用SpringBoot整合Junit进行单元测试

使用

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.xiaohupao;

import cn.xiaohupao.controller.StudentController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@SpringBootTest
public class ApplicationTest {

@Autowired
private StudentController studentController;

@Test
public void testJunit(){
System.out.println(studentController);
}
}

Junit常见问题

@SpringBootTest(classes = HelloApplication.class)可以用来指定启动类

兼容老版本

整合Mybatis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;

INSERT INTO `user`(`id`,`username`,`age`,`address`) VALUES
(2,'pdd',25,'上海'),
(3,'UZI',19,'上海11'),
(4,'RF',19,NULL),
(6,'wk',25,'浙江'),
(8,'lsn',23,'西安'),
(9,'test2',12,'cc2');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.xiaohupao.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

private Integer id;
private String username;
private String address;
}

整合步骤

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--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>

配置数据库信息

1
2
3
4
5
6
7
8
# SPRINGBOOT整合MYBATIS 相关配置

spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

配置mybatis相关配置

1
2
3
4
5
# 配置MYBATIS相关配置

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

编写Mapper接口

注意加上@Mapper和@Repository注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.mapper;

import cn.xiaohupao.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@Mapper
@Repository
public interface UserMapper {

List<User> findAll();
}

编写mapper接口对应的xml文件

1
2
3
4
5
6
7
<?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="cn.xiaohupao.mapper.UserMapper">
<select id="findAll" resultType="cn.xiaohupao.pojo.User">
SELECT * FROM USER
</select>
</mapper>

测试

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
package cn.xiaohupao;

import cn.xiaohupao.controller.StudentController;
import cn.xiaohupao.mapper.UserMapper;
import cn.xiaohupao.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
* @author xiaohupao
* @date 2021/7/12
*/
@SpringBootTest(classes = HelloApplication.class)
public class ApplicationTest {

@Autowired
private UserMapper userMapper;

@Test
public void testFindAll(){
List<User> all = userMapper.findAll();
System.out.println(all);
}
}

Web开发

静态资源访问

可以将静态资源放到resources/static或resources/public或者resources/resources或者resources/META-INF/resources中即可。

例如需要访问:resources/static/index.html只需要在访问资源路径上写成/index.html即可
例如需要访问:resources/static/pages/login.html访问的资源路径写成:/pages/login.html

修改静态资源访问路径

SpringBoot默认的资源路径匹配为/**。如果想要修改可以通过spring.mvc.static-path-pattern这个配置进行修改。
例如想让访问静态资源url必须前缀有/res。例如/res/index.html才能访问到static目录中。我们可以修改如下:在application.yml

1
2
3
spring: 
mvc:
static-path-pattern: /res/**

修改静态资源存放目录

1
2
3
4
5
web:
resources:
static-locations:
- classpath:/wkstatic/
- classpath:/static/

设置请求映射规则@RequestMapping

path或value属性都可以用来指定请求路径
例如:我们期望让请求的资源路径为/test/testPath的请求能够被testPath方法处理则可以写如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@RequestMapping(value = "/test/testPath")
public String testPath(){
return "success";
}
}

我们可以将注解加在方法上也可以加在类上

指定请求方式

1
@RequestMapping(value = "/test/testPath", method=RequestMethod.POST)
  • @PostMapping
  • @GetMapping
  • @PutMapping
  • @DeleteMapping

指定请求参数

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

例如:期望资源的请求路径为/test/testParams的GET请求,并且请求参数中具有code参数的请求能够被testParams方法处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
//@GetMapping(value = "/test/testParams", params = "code!=lsn")
@GetMapping(value = "/test/testParams", params = "code=lsn")
public String testParams(){
return "testParams";
}
}
//http://localhost:8080/test?code=lsn

指定请求头

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

例如:期望请求资源路径为/test/testHeaders的GET请求,并且请求头中具有deviceType的请求能够被testHeaders方法处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
//@GetMapping(value = "/test/testHeaders", headers = "deviceType!=ios")
@GetMapping(value = "/test/testHeaders", headers = "deviceType=ios")
public String testHeaders(){
return "testHeaders";
}
}

指定请求头Content-Type

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

例如:期望让请求资源路径为/test/testConsumes的POST请求,并且请求头中的Content-Type头必须为multipart/from-data的请求能够被testConsumes方法处理:

获取请求参数

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

若想获取这种风格的数据可以使用@PathVariable来实现。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@GetMapping(value = "/user/{id}")
public String findUserById(@PathVariable("id") Integer id){
System.out.println(id);
return "findUserById";
}
}

例如:根据id和username查询用户。请求路径为/user,请求方式为Get,而请求参数id和name要写在请求路径上,例如/user/1/lsn:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@GetMapping(value = "/user/{id}/{userName}")
public String findUserByName(@PathVariable("id") Integer id, @PathVariable("userName") String userName){
System.out.println(id);
System.out.println(userName);
return "findUserByName";
}
}

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

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

Spring-boot的web启动器已经默认导入了jackson依赖,不需要格外导入。

使用

例如:定义一个RestFul风格接口,该接口可以新建用户。请求路径为/user,请求方式为POST。用户数据会转成json通过请求体传递

1
2
3
4
{
"id":2,
"userName":"lsn"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@PostMapping(value = "/user")
public String insertUser(@RequestBody User user){
System.out.println(user);
return "insertUser";
}
}
//{ "username" : "lsn", "age": 18}
//User(id=null, username=lsn, age=18, address=null)

获取参数封装成Map集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@PostMapping(value = "/user")
public String insertUser(@RequestBody Map user){
System.out.println(user);
return "insertUser";
}
}

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

获取QueryString格式参数

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

参数单独的获取

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(Integer id, String name, String[] likes){
System.out.println(id);
System.out.println(name);
System.out.println(likes);
return "testRequestParam";
}
}

可以使用@RequestParam注解解决方法参数名和请求参数名不一致的问题。

获取参数封装成实体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
* @author xiaohupao
* @date 2021/7/11
*/
@RestController
public class HelloController {
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(User user){
System.out.println(user);
return "testRequestParam";
}
}

相关注解其他属性

required

代表是否必须,默认值为true也就是必须要有对应的参数,如果没有就会报错。如果对应的参数可传可不传则可以把其设置为false。

defaultValue

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

响应体相应数据

无论是RestFul风格还是我们之前web接触的异步请求,都需要把json放入响应体中

数据放到响应体

我们的SpringMVC为我们提供了@ResponseBody将把json放到响应体中

@ResponseBody可以加在类上和方法上

数据转换成Json

SpringMVC可以帮我们进行json的转换,不过需要进行相应配置

使用

只要把转换的数据直接作为方法的返回值返回即可。SpringMVC会帮我们把返回值转换成JSON。

例如:要求定义一个RestFul风格的接口,该接口可以用来根据ID查询用户,请求路径为/response/user,请求方式为GET。请求参数id要求写在请求路径上,例如/response/user/1这里i就是id。要求获取参数id,去查询对应id的用户信息。

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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 21:57
*/
@RestController
public class ResponseController {

@GetMapping(value = "/response/user/{id}")
public User findUserById(@PathVariable Integer id){
return new User(id, "lsn", 18, "XA");
}
}
//http://localhost:8080/response/user/2
/**
{
"id": 2,
"username": "lsn",
"age": 18,
"address": "XA"
}
*/

查询用户接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.xiaohupao.mapper;

import cn.xiaohupao.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 20:22
*/
@Mapper
@Repository
public interface UserMapper {

List<User> findAll();
}
1
2
3
4
5
6
7
<?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="cn.xiaohupao.mapper.UserMapper">
<select id="findAll" resultType="cn.xiaohupao.pojo.User">
SELECT * FROM user;
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.xiaohupao.service;

import cn.xiaohupao.pojo.User;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:16
*/
public interface UserService {

List<User> findAll();
}
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
package cn.xiaohupao.service.impl;

import cn.xiaohupao.mapper.UserMapper;
import cn.xiaohupao.pojo.User;
import cn.xiaohupao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:18
*/
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Override
public List<User> findAll() {
return userMapper.findAll();
}
}
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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.User;
import cn.xiaohupao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:09
*/
@RestController
@RequestMapping(value = "/user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping(value = "/findAll")
public List<User> findAllUser(){
return userService.findAll();
}
}
1
http://localhost:8080/user/findAll
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
[
{
"id": 1,
"username": "pdd",
"age": 25,
"address": "上海"
},
{
"id": 2,
"username": "UZI",
"age": 19,
"address": "上海11"
},
{
"id": 3,
"username": "RF",
"age": 19,
"address": null
},
{
"id": 4,
"username": "wk",
"age": 25,
"address": "浙江"
},
{
"id": 5,
"username": "lsn",
"age": 23,
"address": "西安"
},
{
"id": 6,
"username": "test2",
"age": 12,
"address": "cc2"
}
]

接口响应格式统一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package cn.xiaohupao.pojo;

import com.fasterxml.jackson.annotation.JsonInclude;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:30
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult <T>{

/**
* 状态码
*/
private Integer code;

/**
* 提示信息,若有错误,可以根据该字段进行提示
*/
private String msg;

/**
* 存放数据
*/
private T data;

public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}

public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}

public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
}
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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.ResponseResult;
import cn.xiaohupao.pojo.User;
import cn.xiaohupao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:09
*/
@RestController
@RequestMapping(value = "/user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping(value = "/findAll")
public ResponseResult<List<User>> findAllUser(){
List<User> users = userService.findAll();
return new ResponseResult<>(200, "users", users);
}
}
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
http://localhost:8080/user/findAll

{
"code": 200,
"msg": "users",
"data": [
{
"id": 1,
"username": "pdd",
"age": 25,
"address": "上海"
},
{
"id": 2,
"username": "UZI",
"age": 19,
"address": "上海11"
},
{
"id": 3,
"username": "RF",
"age": 19,
"address": null
},
{
"id": 4,
"username": "wk",
"age": 25,
"address": "浙江"
},
{
"id": 5,
"username": "lsn",
"age": 23,
"address": "西安"
},
{
"id": 6,
"username": "test2",
"age": 12,
"address": "cc2"
}
]
}

跨域请求

什么是跨域请求

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

CORS解决跨域

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

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

SpringBoot使用CORS解决跨域

①使用@CrossOrigin

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

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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.ResponseResult;
import cn.xiaohupao.pojo.User;
import cn.xiaohupao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:09
*/
@RestController
@RequestMapping(value = "/user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping(value = "/findAll")
@CrossOrigin
public ResponseResult<List<User>> findAllUser(){
List<User> users = userService.findAll();
return new ResponseResult<>(200, "users", users);
}
}

使用WebMvcConfigurer的addCorsMappings方法配置CorsInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
}
}

登录校验流程介绍

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

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

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

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

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

Token生成方案-JWT

引入依赖

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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();
}


}
1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Exception {

String token = JwtUtil.createJWT(UUID.randomUUID().toString(), "lsn", null);
System.out.println(token);
Claims claims = JwtUtil.parseJWT(token);
String subject = claims.getSubject();
System.out.println(subject);

}

登录接口

Controller

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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.ResponseResult;
import cn.xiaohupao.pojo.SystemUser;
import cn.xiaohupao.service.SystemUserService;
import cn.xiaohupao.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;

/**
* @Author: xiaohupao
* @Date: 2021/8/13 22:26
*/
@RestController
@RequestMapping(value = "/sys_user")
public class SystemUserController {

@Autowired
private SystemUserService systemUserService;

@PostMapping(value = "/login")
public ResponseResult login(@RequestBody SystemUser systemUser){
//校验用户名密码是否正确
SystemUser loginUser = systemUserService.login(systemUser);

Map<String, Object> map;
//如果正确生成taken并返回
if (loginUser != null){
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

1
2
3
4
5
6
7
8
9
10
11
12
package cn.xiaohupao.service;

import cn.xiaohupao.pojo.SystemUser;

/**
* @Author: xiaohupao
* @Date: 2021/8/13 22:34
*/
public interface SystemUserService {

SystemUser login(SystemUser systemUser);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.xiaohupao.service.impl;

import cn.xiaohupao.mapper.SystemUserMapper;
import cn.xiaohupao.pojo.SystemUser;
import cn.xiaohupao.service.SystemUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @Author: xiaohupao
* @Date: 2021/8/13 22:36
*/
@Service
public class SystemUserServiceImpl implements SystemUserService {

@Autowired
private SystemUserMapper systemUserMapper;

@Override
public SystemUser login(SystemUser systemUser) {
SystemUser loginUser = systemUserMapper.login(systemUser);
return loginUser;
}
}

Dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.xiaohupao.mapper;

import cn.xiaohupao.pojo.SystemUser;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
* @Author: xiaohupao
* @Date: 2021/8/13 22:40
*/
@Mapper
@Repository
public interface SystemUserMapper {

SystemUser login(SystemUser systemUser);
}
1
2
3
4
5
6
7
<?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="cn.xiaohupao.mapper.SystemUserMapper">
<select id="login" resultType="cn.xiaohupao.pojo.SystemUser">
SELECT * FROM sys_user WHERE username = #{username} AND password = #{password}
</select>
</mapper>

Test

1
http://localhost:80/sys_user/login
1
2
3
4
{
"username" : "lsn",
"password": "lsn19981025"
}
1
2
3
4
5
6
7
{
"code": 200,
"msg": "登陆成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2ZjNlZGNhMy03YTYwLTRlYzQtYWY1Ni04ZWE1NzY5ZjEyYmMiLCJzdWIiOiIyIiwiaXNzIjoic2ciLCJpYXQiOjE2Mjg4NjYzNjMsImV4cCI6MTYyODg2OTk2M30.AlLWo_lwdtQ8ONs7AceDrc5uX8NbztNfUSVQ-dv6ubk"
}
}

拦截器

应用场景

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

拦截器和过滤器的区别

过滤器是在Servlet执行之前或者之后进行处理。而拦截器是对Handler执行前后进行处理

创建拦截器并配置拦截器

①创建类实现HandlerInterceptor接口

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
package cn.xiaohupao.interceptor;

import cn.xiaohupao.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @Author: xiaohupao
* @Date: 2021/8/14 20:43
*/

/**
* 登录校验拦截器
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求头中的token
String token = request.getHeader("token");
//判断token是否为空
if (!StringUtils.hasText(token)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
//解析token,看看是否成功
try {
//解析过程中没有出现异常说明是登录状态
Claims claims = JwtUtil.parseJWT(token);
} catch (Exception e) {
e.printStackTrace();
//出现了异常说明未登录,提醒重新登录(401)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}

return true;
}
}

配置拦截器

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
package cn.xiaohupao.config;

import cn.xiaohupao.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* @Author: xiaohupao
* @Date: 2021/8/14 20:55
*/
@Configuration
public class LoginConfig implements WebMvcConfigurer {

@Autowired
private LoginInterceptor loginInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(loginInterceptor).
//配置拦截路径
addPathPatterns("/**").
//配置排除路径
excludePathPatterns("/sys_user/login");
}
}

异常统一处理

②@ControllerAdvice

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

1
2
3
4
5
6
7
8
9
10
11
package cn.xiaohupao.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;

/**
* @Author: xiaohupao
* @Date: 2021/8/15 17:30
*/
@ControllerAdvice
public class MyControllerAdvice {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.xiaohupao.exception;

import cn.xiaohupao.pojo.ResponseResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* @Author: xiaohupao
* @Date: 2021/8/15 17:30
*/
@ControllerAdvice
public class MyControllerAdvice {

@ExceptionHandler(RuntimeException.class)
@ResponseBody
public ResponseResult handlerException(Exception e){
//获取异常信息,存放如ResponseResult的msg属性
String message = e.getMessage();
ResponseResult result = new ResponseResult(300, message);
return result;
}
}

获取web原生对象

我们经常要使用到request对象,response,session对象等。我们也可以通过SpringMVC获取到这些对象。我们只需要在方法上添加对应类型的参数类型即可,SpringMVC会把我们需要的对象传给我们的形参。

1
2
3
4
5
@RequestMapping(value = "/getRequestAndResponse")
public ResponseResult test(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println(request);
return new ResponseResult(200, "success");
}

自定义参数解析

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
package cn.xiaohupao.controller;

import cn.xiaohupao.pojo.ResponseResult;
import cn.xiaohupao.pojo.User;
import cn.xiaohupao.service.UserService;
import cn.xiaohupao.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/12 22:09
*/
@RestController
@RequestMapping(value = "/user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping(value = "/findAll")
@CrossOrigin
public ResponseResult<List<User>> findAllUser(HttpServletRequest request) throws Exception {
//获取请求头中的token
String token = request.getHeader("token");
if (StringUtils.hasText(token)){
//解析token获取用户id
Claims claims = JwtUtil.parseJWT(token);
String userId = claims.getSubject();
System.out.println(userId);
}
List<User> users = userService.findAll();
return new ResponseResult<>(200, "users", users);
}
}

如果想要实现像获取请求体中的数据那样,在Handler方法的参数上增加一个@ResponseBody注解就可以获取到对应的数据的话。可以使用HandlerMethodArgumentResolver来实现自定义的参数解析。

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
package cn.xiaohupao.resolver;

import cn.xiaohupao.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
* @Author: xiaohupao
* @Date: 2021/8/17 22:11
*/
@Component
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {

//判断方法参数使用当前的参数解析器进行解析
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//如果方法参数加上了CurrentUserId注解,就能被我们的解析器解析
return methodParameter.hasParameterAnnotation(CurrentUserId.class);
}

//参数进行解析的方法,可以在方法中获取对应的数据,把数据作为返回值返回。方法返回值就会赋值给对应的方法参数
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
//获取请求头中的token
String token = nativeWebRequest.getHeader("token");
if (StringUtils.hasText(token)){
//解析token,获取userID
Claims claims = JwtUtil.parseJWT(token);
String userId = claims.getSubject();
return userId;
}
//返回结果
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.xiaohupao.resolver;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @Author: xiaohupao
* @Date: 2021/8/17 22:18
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUserId {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.xiaohupao.config;

import cn.xiaohupao.resolver.UserIdArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
* @Author: xiaohupao
* @Date: 2021/8/17 22:26
*/
public class ArgumentResolverConfig implements WebMvcConfigurer {

@Autowired
private UserIdArgumentResolver userIdArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userIdArgumentResolver);
}
}
Donate comment here