SpringBoot
Author:Exchanges
Version:9.0.0
- 一、SpringBoot介绍
- 二、SpringBoot快速入门
- 三、SpringBoot热部署配置
- 四、SpringBoot中的默认配置
- 五、SpringBoot整合MyBatis【重点】
- 六、SpringBoot中的异常处理
- 七、SpringBoot中的过滤器(Listener操作同理)
- 八、SpringBoot中的拦截器
- 九、SpringBoot整合Thymeleaf
- 十、SpringBoot整合FreeMarker
- 十一、SpringBoot整合Redis【重点】
- 十二、SpringBoot整合Quartz以及异步方法调用
- 十三、SpringBoot日志
- 十四、SpringBoot整合Swagger
- 十五、SpringBoot整合knife4j
一、SpringBoot介绍
1.1 SpringBoot简介
SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。
Spring Boot是基于约定优于配置的,主要作用就是用来简化Spring应用的初始搭建以及开发过程!
后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
1.2 SpringBoot的特点
1.基于Spring的开发提供更快的入门体验。
2.开箱即用,没有代码生成,也无需XML配置,同时也可以修改默认值来满足特定的需求。
3.提供了一些大型项目中常见的非功能性特性,外部配置等。
4.SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
5.SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了
6.SpringBoot默认只有一个.properties的配置文件,不推荐使用xml,后期会采用.yml的文件去编写配置信息。
7.SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。
8.后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
1.3 SpringBoot的核心功能
1.起步依赖
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
2.自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
二、SpringBoot快速入门
网站创建地址:https://start.spring.io/ 或者https://start.springboot.io/
项目创建完成!
此时pom.xml文件中会自动导入springboot所需依赖,并且在src下会生成一个配置类。
注意:若pom.xml中依赖无法下载,需要修改maven工程对应的settings.xml文件,找到settings.xml文件中的镜像配置,原因是maven中央仓库下载不下来springboot关联的架包,所以建议使用阿里云的镜像.
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The
repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror.
IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
|
-->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus-aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
运行配置类,看到如下页面,表示启动成功!
手动编写Controller进行进一步测试(注意:需要将controller类,放在启动类的子包中或者同级包下)
package com.qf.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/login")
public String login(){
System.out.println("登录");
return "success";
}
}
重新启动配置类,访问:http://localhost:8080/login
三、SpringBoot热部署配置
为了方便开发,可以在创建项目时手动勾选热部署,或导入该依赖,就不需要每次重启配置类
<!--热部署配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
配置自动编译
然后按住Shift+Ctrl+Alt+/,选择Registry(选完之后再次查看一下是否勾选上),如果IDEA没有该选项可以不用操作
配置热部署
先启动,然后添加一行代码,服务器会自动重启
四、SpringBoot中的默认配置
可以从jar包中找到SpringBoot的默认配置文件位置
SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置,SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件。
其中,application.properties文件是键值对类型的文件,除此之外,SpringBoot还可以使用yml文件进行配置,YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁,YML文件的扩展名可以使用.yml或者.yaml。
4.1 application.properties方式修改默认配置
4.2 application.yml方式修改默认配置(注意:yml文件中空格表示层级关系)
yml文件支持的配置
#普通数据的配置
name: jack
#对象的配置
user:
username: rose
password: 123
#配置数组
array:
beijing,
tianjin,
shanghai
#配置集合
yangl:
test:
name: tom
arr: 1,jack,2,tom
list1: #这种对象形式的,只能单独写一个对象去接收,所以无法使用@value注解获取
- zhangsan
- lisi
list2:
- driver: mysql
port: 3306
- driver: oracle
port: 1521
map:
key1: value1
key2: value2
把yml文件中配置的内容注入到成员变量中,
第一种方式,创建UserController,使用@Value注解方式注入
package com.qf.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class UserController {
@Value("${name}")
private String name;
@Value("${user.username}")
private String username;
@Value("${user.password}")
private String password;
@Value("${array}")
private String [] array;
@RequestMapping("/test")
public String[] test(){
System.out.println(name);
System.out.println(username);
System.out.println(password);
System.out.println(array[0]);
return array;
}
}
第二种方式,使用@ConfigurationProperties注解方式,提供GET/SET方法
创建YmlController
package com.qf.controller;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@RestController
@ConfigurationProperties(prefix = "yangl.test")
public class YmlController {
private String name;
private String[] arr;
private List<String> list1;
private List<Map<String,String>> list2;
private Map<String,String> map;
@RequestMapping("/yml")
public void yml(){
System.out.println(name);
System.out.println(Arrays.toString(arr));
System.out.println(list1);
System.out.println(list2);
System.out.println(map);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getArr() {
return arr;
}
public void setArr(String[] arr) {
this.arr = arr;
}
public List<String> getList1() {
return list1;
}
public void setList1(List<String> list1) {
this.list1 = list1;
}
public List<Map<String, String>> getList2() {
return list2;
}
public void setList2(List<Map<String, String>> list2) {
this.list2 = list2;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
如果使用@ConfigurationProperties注解时提示以下信息
导入以下依赖即可(也可以不导入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
4.3 设置不同环境下的application.yml配置文件
1.创建application-dev.yml,并编写以下内容
server:
port: 8081
2.创建application-pro.yml,并编写以下内容
server:
port: 8082
3.创建application.yml,可以配置使用不同的文件,- dev 或者 - pro
spring:
profiles:
active:
- dev
五、SpringBoot整合MyBatis【重点】
5.1 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
5.2 在application.properties中配置数据库信息
#配置数据库
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/wyy_music?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#mybatis
#指定mapper.xml文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
#指定控制台输出日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
也可使用yml方式配置,需要创建application.yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/wyy_music?serverTimezone=Asia/Shanghai&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
5.3 使用之前的Music表,创建实体类
Music
package com.qf.pojo;
import lombok.Data;
@Data
public class Music {
private Integer musicId;
private String musicName;
private String musicAlbumName;
private String musicAlbumPicurl;
private String musicMp3url;
private String musicArtistName;
private Integer sheetId;
}
5.4 创建mapper
MusicMapper
package com.qf.mapper;
import com.qf.pojo.Music;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MusicMapper {
List<Music> findAll();
}
5.5 在src\main\resources\mapper路径下创建对应的MusicMapper.xml文件
MusicMapper.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.qf.mapper.MusicMapper">
<resultMap id="musicMap" type="com.qf.pojo.Music">
<id property="musicId" column="music_id"></id>
<result property="musicName" column="music_name"></result>
<result property="musicAlbumName" column="music_album_name"></result>
<result property="musicAlbumPicurl" column="music_album_picUrl"></result>
<result property="musicMp3url" column="music_mp3Url"></result>
<result property="musicArtistName" column="music_artist_name"></result>
<result property="sheetId" column="sheet_id"></result>
</resultMap>
<sql id="BaseSql">
select music_id,music_name,music_album_name,music_album_picUrl,music_mp3Url,music_artist_name,sheet_id from tb_music
</sql>
<select id="findAll" resultMap="musicMap">
<include refid="BaseSql"></include>
</select>
</mapper>
5.6 创建service
MusicService
package com.qf.service;
import com.qf.pojo.Music;
import java.util.List;
public interface MusicService {
List<Music> findAll();
}
5.7 创建serviceImpl
MusicServiceImpl
package com.qf.service.impl;
import com.qf.mapper.MusicMapper;
import com.qf.pojo.Music;
import com.qf.service.MusicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MusicServiceImpl implements MusicService {
@Autowired
private MusicMapper musicMapper;
@Override
public List<Music> findAll() {
return musicMapper.findAll();
}
}
5.8创建controller
MusicController
package com.qf.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qf.pojo.Music;
import com.qf.service.MusicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("music")
public class MusicController {
@Autowired
private MusicService musicService;
@RequestMapping("findAll")
public List<Music> findAll(){
return musicService.findAll();
}
}
5.9启动配置类(注意:需要在启动类上配置@MapperScan并扫描Mapper对应的包名)
package com.qf;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.qf.mapper")//扫描mapper
public class Springboot03Application {
public static void main(String[] args) {
SpringApplication.run(Springboot03Application.class, args);
}
}
启动测试即可!
5.10分页插件的使用
5.10.1导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
5.10.2添加方法
//分页
@RequestMapping("findByPage")
public PageInfo findByPage(
@RequestParam(value = "pageNum",required = false,defaultValue = "1")Integer pageNum,
@RequestParam(value = "pageSize",required = false,defaultValue = "2")Integer pageSize){
PageHelper.startPage(pageNum,pageSize);
List<Music> musicList = musicService.findAll();
PageInfo<Music> musicPageInfo = new PageInfo<>(musicList);
return musicPageInfo;
}
5.10.3在application.properties中添加配置
#配置分页
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
启动测试即可!
5.11 AOP的使用
5.11.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
5.11.2 创建通知类
例如:创建一个日志的通知类,编写前置通知方法测试...
package com.qf.advice;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class LogAspectHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义一个切面,拦截com.qf.controller包和子包下的所有方法
*/
@Pointcut("execution(* com.qf.controller..*.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void doBefore() {
logger.info("调用目标方法前执行...");
//以用来记录一些信息,比如获取请求的url和ip
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 获取请求url
String url = request.getRequestURL().toString();
// 获取请求ip
String ip = request.getRemoteAddr();
logger.info("用户请求的url为:{},ip地址为:{}", url, ip);
}
}
5.12 SpringBoot基于OSS上传
5.12.1 导入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
5.12.2 配置OSS参数
#配置OSS的个人信息
bucketName: 自己的bucketName
accessKeyId: 自己的accessKeyId
accessKeySecret: 自己的accessKeySecret
#OSS对应的区域
endpoint: oss-cn-beijing.aliyuncs.com
#OSS对应的文件夹,会在OSS中自动创建
filehost: image
5.12.3 编写页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8"/>
<title>【基于OSS的上传文件页面】</title>
</head>
<body>
<form action="/uploadFile" enctype="multipart/form-data" method="post">
文件:<input type="file" id="exampleInputFile" name="file"/><br>
<input type="submit" value="上传">
</form>
</body>
</html>
5.12.4 编写封装配置信息类
package com.qf.oss;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* 把配置文件中的配置信息读取到该类中.
*/
@Data
@Configuration
public class OssConfiguration {
@Value("${endpoint}")
private String endPoint;
@Value("${accessKeyId}")
private String accessKeyId;
@Value("${accessKeySecret}")
private String accessKeySecret;
@Value("${filehost}")
private String fileHost;
@Value("${bucketName}")
private String bucketName;
}
5.12.5 编写上传工具类
package com.qf.oss;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* 封装文件上传方法
*/
@Component
public class AliyunOssUtil {
@Autowired
private OssConfiguration config;
public String upload(File file) {
if (file == null) {
return null;
}
String endPoint = config.getEndPoint();
String keyId = config.getAccessKeyId();
String keySecret = config.getAccessKeySecret();
String bucketName = config.getBucketName();
String fileHost = config.getFileHost();
//定义子文件的格式
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = format.format(new Date());
//阿里云文件上传客户端
OSSClient client = new OSSClient(endPoint, keyId, keySecret);
try {
//判断桶是否存在
if (!client.doesBucketExist(bucketName)) {
//创建桶
client.createBucket(bucketName);
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
//设置访问权限为公共读
createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
//发起创建桶的请求
client.createBucket(createBucketRequest);
}
//当桶存在时,进行文件上传
//设置文件路径和名称
String fileUrl = fileHost + "/" + (dateStr + "/" + UUID.randomUUID().toString().replace("-", "") + "-" + file.getName());
PutObjectResult result = client.putObject(new PutObjectRequest(bucketName, fileUrl, file));
client.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
//文件上传成功后,返回当前文件的路径
if (result != null) {
return fileUrl;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
client.shutdown();
}
}
return null;
}
}
5.12.6 编写Controller
package com.qf.oss;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
@Controller("oss")
public class OssController {
@Autowired
private AliyunOssUtil ossUtil;
@PostMapping("/uploadFile")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file) {
System.out.println(file.getOriginalFilename()+"---------");
try {
if (file != null) {
String fileName = file.getOriginalFilename();
if (!"".equals(fileName.trim())) {
File newFile = new File(fileName);
FileOutputStream os = new FileOutputStream(newFile);
os.write(file.getBytes());
os.close();
//把file里的内容复制到奥newFile中
file.transferTo(newFile);
String upload = ossUtil.upload(newFile);
//图片回显地址:
System.out.println("path=" + upload);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "success";
}
}
上传成功后,可获取对应地址,在HTML页面中通过Img标签显示!
5.13 SpringBoot工程打包部署
双击package执行打包命令,如果打包时报错,需要修改pom.xml文件中的build标签
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
改为:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--若打包失败,可安装此插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
或者:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.4.RELEASE</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4</version>
</plugin>
</plugins>
</build>
然后再次打包,在对应的磁盘目录找到对应的xxx.jar文件
部署:
修改项目中的数据库相关配置,然后启动Linux上的mysql,把xxx.jar文件拷贝到Linux中的任意目录
java -jar xxx.jar Linux上启动springboot工程
java -jar -Dserver.port=1234 xxx.jar Linux上指定端口号启动springboot工程后台启动:
使用以上命令启动成功后,输入:ctrl + z,输入:bg,输入:exit;
六、SpringBoot中的异常处理
6.1 创建自定义异常类
MyException
package com.qf.exception;
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
6.2 在Controller测试异常
在Controller添加方法
//测试异常
@RequestMapping("add")
public String add(){
musicService.add();
return "success";
}
//测试自定义异常
@RequestMapping("delete")
public String deleteUser() throws MyException {
musicService.delete();
return "success";
}
6.3 创建Service接口以及实现类
在Service添加方法
void add();
void delete()throws MyException;
在ServiceImpl添加方法
@Override
public void add() {
System.out.println("add...");
int i = 1/0;
}
@Override
public void delete() throws MyException {
System.out.println("delete...");
throw new MyException("自定义异常");
}
6.4 创建全局异常处理类
MyExceptionHandler
package com.qf.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public Map<String,Object> hander1(Exception e, HttpServletRequest request){
System.out.println("hander1...");
Map<String, Object> map = new HashMap<>();
map.put("code",-1);
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
@ExceptionHandler(MyException.class)
public Map<String,Object> hander2(MyException e, HttpServletRequest request){
System.out.println("hander2...");
Map<String, Object> map = new HashMap<>();
map.put("code",-1);
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
}
访问测试即可!
七、SpringBoot中的过滤器(Listener操作同理)
7.1 创建过滤器
LoginFilter
package com.qf.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入LoginFilter之前...");
//放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("进入LoginFilter之后...");
}
}
7.2 创建Controller中添加方法测试
package com.qf.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("user")
public class UserController {
//测试过滤器
@RequestMapping("login")
public String testFilter(){
System.out.println("登录");
return "success";
}
}
7.3 在启动类添加@ServletComponentScan注解
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@MapperScan("com.qf.mapper")//扫描mapper
@ServletComponentScan//Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
public class Springboot02Application {
public static void main(String[] args) {
SpringApplication.run(Springboot02Application.class, args);
}
}
访问测试即可!
八、SpringBoot中的拦截器
8.1 创建自定义拦截器
package com.qf.interceptor;
import org.omg.PortableInterceptor.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//进入controller方法之前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;//true表示放行,false表示不放行
}
//调用完controller之后,视图渲染之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
//页面跳转之后,整个流程执行之后,一般用于资源清理操作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
8.2 创建拦截器配置类
package com.qf.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截器并指定拦截路径
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/login");
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");//拦截所有
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login");//指定不拦截
//添加自定义拦截器
WebMvcConfigurer.super.addInterceptors(registry);
}
}
访问对应方法测试即可!
8.3 跨域拦截器配置(补充)
配置后不需要在每一个Controller上面写@CrossOrigin("*")注解了
8.3.1 编写拦截器
package com.qf.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class CorsInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 此处配置的是允许任意域名跨域请求,可根据需求指定
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "86400");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization");
return true;
}
}
8.3.2 编写拦截器配置类
package com.qf.config;
import com.qf.interceptor.CorsInterceptor;
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;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CorsInterceptor corsInterceptor;
/**
* 配置springboot拦截注册
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
}
}
九、SpringBoot整合Thymeleaf
9.1导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
9.2在application.properties文件添加配置
########## 配置thymeleaf ##########
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.servlet.content-type=text/html
9.3创建实体类
package com.qf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
}
9.4创建Controller
package com.qf.controller;
import com.qf.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.Date;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("findAll")
public String findAll(Model model){
//模拟数据库查询
ArrayList<User> users = new ArrayList<>();
users.add(new User(1001,"张三",new Date()));
users.add(new User(1002,"李四",new Date()));
users.add(new User(1003,"王五",new Date()));
users.add(new User(1004,"赵六",new Date()));
model.addAttribute("users",users);
return "/list";
}
@RequestMapping("findById")
public String findById(Integer uid,Model model){
System.out.println(uid);
//模拟数据库查询
User db_user = new User(uid,"杰克",new Date());
model.addAttribute("user",db_user);
return "/queryOne";
}
}
9.5在templates目录下创建list.html以及queryOne.html
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:if="${users!=null}">
<table border="1" width="600">
<tr th:each="user,state : ${users}">
<td th:text="${state.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!-- 第二个变量,可以获取遍历的元素的状态-->
<tr th:each="user,state : ${users}">
<td th:text="${state.index}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!--如果不设置表示状态的变量,默认遍历的元素的变量名+Stat
表示状态的变量-->
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-
dd HH:mm:ss')}"></td>
</tr>
</table>
</div>
<hr color="red">
<table border="1" width="600">
<tr>
<th>序号</th>
<th>姓名</th>
<th>生日</th>
<th>详情</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td><a th:href="@{/user/findById(uid=${user.id})}">查询</a></td>
</tr>
</table>
</body>
</html>
queryOne.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:object="${user}" action="http://www.baidu.com">
<!-- *{...}选择表达式一般跟在th:object后,直接选择object中的属性-->
<input type="hidden" th:id="*{id}" name="id">
用户名:<input type="text" th:value="*{username}" name="username" /><br /><br />
生日:<input type="date" th:value="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}" name="birthday"/><br /><br />
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</body>
</html>
十、SpringBoot整合FreeMarker
10.1导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
10.2在application.properties文件添加配置
#配置freemarker
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
#缓存
spring.freemarker.cache=false
#加载路径(前缀)
spring.freemarker.template-loader-path=classpath:/templates
#后缀(不能省略)
spring.freemarker.suffix=.ftl
10.3创建实体类
package com.qf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
private String address;
}
10.4在templates目录下创建student.ftl文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1" width=600>
<tr>
<th>index</th>
<th>id</th>
<th>name</th>
<th>age</th>
<th>address</th>
</tr>
<#list students as student>
<#if student_index % 2 == 0>
<tr bgcolor="red">
<#else>
<tr bgcolor="yellow">
</#if>
<td>${student_index}</td>
<td>${student.id}</td>
<td>${student.name}</td>
<td>${student.age}</td>
<td>${student.address}</td>
</tr>
</#list>
</table>
</body>
</html>
10.5创建controller
package com.qf.controller;
import com.qf.pojo.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@Controller
@RequestMapping("student")
public class StudentController {
@RequestMapping("findAll")
public String findAll(Model model){
System.out.println("findAll...");
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"张三",20,"二七区"));
students.add(new Student(2,"李四",21,"金水区"));
students.add(new Student(3,"王五",22,"中原区"));
students.add(new Student(4,"赵六",23,"经开区"));
model.addAttribute("students",students);
return "/student";
}
//网页静态化,提高访问效率
//生成静态页面
@RequestMapping("createHtml")
@ResponseBody
public String createHtml() throws Exception {
//准备数据
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"张三",20,"二七区"));
students.add(new Student(2,"李四",21,"金水区"));
students.add(new Student(3,"王五",22,"中原区"));
students.add(new Student(4,"赵六",23,"经开区"));
//创建Map
HashMap<String, Object> map = new HashMap<>();
map.put("students",students);
//创建配置类
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//设置字符集
configuration.setDefaultEncoding("utf-8");
//设置要加载的模版目录
configuration.setDirectoryForTemplateLoading(new File("D:\\java2111\\springboot-03\\src\\main\\resources\\templates"));
//获取某一个模板
Template template = configuration.getTemplate("student.ftl");
//创建输出流
FileWriter fileWriter = new FileWriter("D:\\java2111\\springboot-03\\src\\main\\resources\\static\\student.html");
//创建页面
template.process(map,fileWriter);
//关流
fileWriter.close();
return "success";
}
}
启动工程,先访问 http://localhost:8080/student/createHtml,然后点击IDEA查看static是否生成student.html页面,如果生成后,则可以直接访问 http://localhost:8080/student.html 查看页面
十一、SpringBoot整合Redis【重点】
11.1导入依赖
<!-- 配置redis所需要的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
11.2创建application.yml文件并添加配置
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
lettuce:
pool:
max-active: 100
11.3创建User类
package com.qf.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private Integer id;
private String name;
private String password;
}
11.4导入Redis序列化配置类
package com.qf.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig{
/**
* springboot 默认帮我们创建的RedisTemplate的key和value的序列化方式是jdk默认的方式,
* 我们有时候手动向redis中添加的数据可能无法被查询解析出来,所以我们需要修改序列化方式
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer); //设置key的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);//设置hash类型的数据的key的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//非final类型的数据才会被序列化
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);//设置value的序列化方式为json
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
11.5导入Redis工具类
package com.qf.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time, TimeUnit timeUnit) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time, TimeUnit timeUnit) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, TimeUnit timeUnit, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time, timeUnit);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time,TimeUnit timeUnit) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time,timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time,TimeUnit timeUnit) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time,timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
11.6在项目自带的测试类中测试
package com.qf;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.pojo.User;
import com.qf.utils.RedisUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class Springboot04RedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisUtil redisUtil;
@Test
void contextLoads() {
//对应类型
redisTemplate.opsForValue();//String类型
redisTemplate.opsForList();//List类型
redisTemplate.opsForSet();//Set类型
redisTemplate.opsForZSet();//ZSet类型
redisTemplate.opsForHash();//Hash类型
//redisTemplate测试
// User user = new User();
// user.setId(1001);
// user.setName("张三");
// user.setPassword("123");
//
// redisTemplate.opsForValue().set("user",user);
//
// User db_user = (User)redisTemplate.opsForValue().get("user");
// System.out.println(db_user);
//stringRedisTemplate测试
// User user = new User();
// user.setId(1001);
// user.setName("张三");
// user.setPassword("123");
//
// //转Json
// String jsonUser = null;
// try {
// jsonUser = new ObjectMapper().writeValueAsString(user);
// } catch (JsonProcessingException e) {
// e.printStackTrace();
// }
// //保存数据
// stringRedisTemplate.opsForValue().set("user",jsonUser,30, TimeUnit.SECONDS);
// //获取数据
// String db_jsonUser = stringRedisTemplate.opsForValue().get("user");
// System.out.println(db_jsonUser);
//redisUtil测试
User user = new User();
user.setId(1001);
user.setName("张三");
user.setPassword("123");
redisUtil.set("user",user);
User db_user = (User)redisUtil.get("user");
System.out.println(db_user);
}
}
11.7使用Redis做MyBatis的二级缓存
MyBatis中的缓存分为一级缓存和二级缓存
一级缓存:基于sqlSession的缓存
二级缓存:基于多个sqlSession 共享的namspace数据块
通常一个mapper 都一个namespace,所有的相关二级缓存都存在该namespace 数据块下
查询顺序:先去二级缓存,如果二级没有再去一级缓存,一级没有再去数据库
注意:在spring 配置的mybatis 中不存在一级缓存,二级缓存发生增删改,该namespace 下所有缓存数据 立即清空,目的是为了避免有脏数据存在
步骤如下:
1.在配置文件中开启二级缓存:mybatis.configuration.cache-enabled=true
2.编写ApplicationContextHolder
package com.qf.secondcache;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* 在spring中,只要实现或者继承xxAware接口或者类,在实例该对象时,
* 会调用实现xxAware接口的类的方法,把参数传递
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
//spring容器
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static RedisTemplate getRedisTemplate(){
return ApplicationContextHolder.applicationContext
.getBean("redisTemplate",RedisTemplate.class);
}
}
3.编写RedisCache
package com.qf.secondcache;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 数据查询顺序:二级缓存 -> 一级缓存 -> 数据库
* 我们在mybatis中指定了二级缓存,在mybatis启动会生成Cache对象,
* 如果在该类使用@Autowired注入RedisTemplate是无法注入的,需要使用spring注入
*/
public class RedisCache implements Cache {
//RedisTemplate对象
private RedisTemplate redisTemplate;
//id相当于当前sql对应的cache的命名空间 namespace="com.qf.mapper.xxxMapper"
private String id;
//读写锁:多线程中可以共享锁,如果大家都是读操作,提高数据的读的并发能力
//如果有一个人进行了写操作,其他人都不能进行读写操作了
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//获取RedisTemplate对象
public RedisTemplate getRedisTemplate(){
//判断
if(redisTemplate == null){
synchronized (RedisCache.class){
if(redisTemplate == null){
RedisTemplate redisTemplate = ApplicationContextHolder.redisTemplate();
//设置key使用string类型的序列化方式
redisTemplate.setKeySerializer(RedisSerializer.string());
return redisTemplate;
}
return this.redisTemplate;
}
}
return redisTemplate;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
//构造器
public RedisCache(String id) {
System.out.println("id:"+id);
this.id = id;
}
//id相当于当前sql对应的cache的命名空间
@Override
public String getId() {
System.out.println("getId:"+id);
return id;
}
/**
* 将结果放入缓存
* @param key -> 命名空间 + sql + 参数 = 组成的字符串
* @param value -> sql查询的结果
*/
@Override
public void putObject(Object key, Object value) {
System.out.println("putObject中的key:"+key);
System.out.println("putObject中的value:"+value);
getRedisTemplate().opsForValue().set(key.toString(),value);
}
/**
* 获取缓存中的数据
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
System.out.println("getObject:"+key);
return getRedisTemplate().opsForValue().get(key.toString());
}
/**
* 从缓存中移除数据
* @param key
* @return
*/
@Override
public Object removeObject(Object key) {
System.out.println("removeObject:"+key);
return getRedisTemplate().delete(key.toString());
}
/**
* 清空缓存
*/
@Override
public void clear() {
System.out.println("clear");
Set keys = getRedisTemplate().keys("*" + id + "*");
System.out.println("清空缓存keys:"+keys);
getRedisTemplate().delete(keys);
}
/**
* 获取缓存数据长度
* @return
*/
@Override
public int getSize() {
Set keys = getRedisTemplate().keys("*" + id + "*");
return keys.size();
}
}
4.配置mapper.xml文件
<!-- 开启二级缓存(全局) -->
<!--
type:使用自定义的对象进行存储
blocking:true 查询时是否阻塞加锁
flushInterval: 毫秒值,缓存多久清空一次
eviction: 缓存失效策略: LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
readOnly:true 只读,不能被修改
size: 1024 缓存的大小
-->
<cache type="com.qf.secondcache.RedisCache"></cache>
5.访问查询方法进行测试,可在redis中查看对应生成的key,再调用删除的方法,再次去redis中查看即可
十二、SpringBoot整合Quartz以及异步方法调用
12.1 异步方法调用
1.创建AsyncController
package com.qf.controller;
import com.qf.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping("testAsync")
public String testAsync(){
asyncService.testAsync();
return "AsyncController";
}
}
2.创建AsyncService
package com.qf.service;
public interface AsyncService {
void testAsync();
}
3.创建AsyncServiceImpl
package com.qf.service.impl;
import com.qf.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncServiceImpl implements AsyncService {
@Async//设置当前方法为异步方法
@Override
public void testAsync() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AsyncServiceImpl");
}
}
4.在启动上添加开启异步注解,然后测试
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync//使用异步方法
public class Springboot05QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot05QuartzApplication.class, args);
}
}
12.2使用@Scheduled注解实现定时任务
1.创建任务类,多个任务执行时,默认使用单线程,可以通过异步方法使其为多线程
package com.qf.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Date;
//任务类
@Configuration
public class Tasks {
private Logger logger = LoggerFactory.getLogger(Tasks.class);
@Async
@Scheduled(cron = "*/2 * * * * ?")
public void task1(){
//System.out.println("task1:"+new Date().toLocaleString());
logger.info("task1:"+new Date().toLocaleString());
}
@Async
@Scheduled(cron = "*/3 * * * * ?")
public void task2(){
//System.out.println("task2:"+new Date().toLocaleString());
logger.info("task2:"+new Date().toLocaleString());
}
}
2.在启动上添加开启任务注解,然后测试
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableAsync//使用异步方法
@EnableScheduling//开启任务
public class Springboot05QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot05QuartzApplication.class, args);
}
}
12.3 定时发送邮件
1.导入依赖
<!-- 邮箱 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.使用MailUtils工具类进行测试
package com.qf.utils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.Random;
/**
* 发邮件工具类
*/
public final class MailUtils {
private static final String USER = "729953102@qq.com"; // 发件人邮箱地址
private static final String PASSWORD = "kmrmcaazztfjbefj"; // qq邮箱的客户端授权码(需要发短信获取)
/**
* @param to 收件人邮箱地址
* @param text 邮件正文
* @param title 标题
*/
/* 发送验证信息的邮件 */
public static boolean sendMail(String to, String text, String title) {
try {
final Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", "smtp.qq.com");
// 发件人的账号
props.put("mail.user", USER);
//发件人的密码
props.put("mail.password", PASSWORD);
// 构建授权信息,用于进行SMTP进行身份验证
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// 用户名、密码
String userName = props.getProperty("mail.user");
String password = props.getProperty("mail.password");
return new PasswordAuthentication(userName, password);
}
};
// 使用环境属性和授权信息,创建邮件会话
Session mailSession = Session.getInstance(props, authenticator);
// 创建邮件消息
MimeMessage message = new MimeMessage(mailSession);
// 设置发件人
String username = props.getProperty("mail.user");
InternetAddress form = new InternetAddress(username);
message.setFrom(form);
// 设置收件人
InternetAddress toAddress = new InternetAddress(to);
message.setRecipient(Message.RecipientType.TO, toAddress);
// 设置邮件标题
message.setSubject(title);
// 设置邮件的内容体
message.setContent(text, "text/html;charset=UTF-8");
// 发送邮件
Transport.send(message);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
//随机生成num个数字验证码
public static String getValidateCode(int num) {
Random random = new Random();
String validateCode = "";
for (int i = 0; i < num; i++) {
//0 - 9 之间 随机生成 num 次
int result = random.nextInt(10);
validateCode += result;
}
return validateCode;
}
//测试
public static void main(String[] args) throws Exception {
//给指定邮箱发送邮件
MailUtils.sendMail("729953102@qq.com", "你好,这是一封测试邮件,无需回复。", "测试邮件随机生成的验证码是:" + getValidateCode(6));
System.out.println("发送成功");
}
}
3.编写邮箱任务类
package com.qf.task;
import com.qf.utils.MailUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
@Configuration
public class TaskMail {
//指定时间进行发送邮件
@Scheduled(cron = "10 55 * * * ?")
public void sendMail(){
MailUtils.sendMail("729953102@qq.com", "你好,这是一封测试邮件,无需回复。", "测试邮件随机生成的验证码是:" + MailUtils.getValidateCode(6));
}
}
启动项目测试即可!
12.4整合quartz,首先导入依赖
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.编写任务类
package com.qf.quartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("MyJob:"+ new Date().toLocaleString());
}
}
3.编写配置类
package com.qf.quartz;
import com.qf.quartz.job.MyJob;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class QuartzConfig {
//任务
@Bean
public JobDetailFactoryBean getJobDetailFactoryBean(){
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
//指定任务
jobDetailFactoryBean.setJobClass(MyJob.class);
return jobDetailFactoryBean;
}
//触发器
@Bean
public CronTriggerFactoryBean getCronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
JobDetail jobDetail = jobDetailFactoryBean.getObject();
//指定任务详情
cronTriggerFactoryBean.setJobDetail(jobDetail);
//指定Cron表达式
cronTriggerFactoryBean.setCronExpression("*/3 * * * * ?");
return cronTriggerFactoryBean;
}
//调度器
@Bean
public SchedulerFactoryBean getSchedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
CronTrigger cronTrigger = cronTriggerFactoryBean.getObject();
//指定触发器
schedulerFactoryBean.setTriggers(cronTrigger);
return schedulerFactoryBean;
}
}
4.启动测试类测试即可
十三、SpringBoot日志
SpringBoot默认使用的日志是Logback,官方建议日志文件命名为:logback-spring.xml
13.1在resources目录下创建logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。
默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<!-- 定义变量,可通过 ${log.path}和${CONSOLE_LOG_PATTERN} 得到变量值 -->
<property name="log.path" value="D:/log" />
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} |-[%-5p] in %logger.%M[line-%L] -%m%n"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- Threshold=即最低日志级别,此appender输出大于等于对应级别的日志
(当然还要满足root中定义的最低级别)
-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<!-- 日志格式(引用变量) -->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 追加到文件中 -->
<appender name="file1" class="ch.qos.logback.core.FileAppender">
<file>${log.path}/mylog1.log</file>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 滚动追加到文件中 -->
<appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/mylog2.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录
文件超过最大尺寸后,会新建文件,然后新的日志文件中继续写入
如果日期变更,也会新建文件,然后在新的日志文件中写入当天日志
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 新建文件后,原日志改名为如下 %i=文件序号,从0开始 -->
<fileNamePattern>${log.path}/newlog-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每个日志文件的最大体量 -->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1kb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数,1=则只保留昨天的归档日志文件 ,不设置则保留所有日志-->
<maxHistory>1</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="file1"/>
<appender-ref ref="file2"/>
</root>
</configuration>
启动工程,进行测试
十四、SpringBoot整合Swagger
14.1 Swagger介绍
现在开发,很多采用前后端分离的模式,前端只负责调用接口,进行渲染,前端和后端的唯一联系,变成了API接口。因此,API文档变得越来越重要。swagger是一个方便我们更好的编写API文档的框架,而且swagger可以模拟http请求调用。
大部分采取的方式:Vue + SpringBoot,Vue通过js渲染页面,后端把数据传递给js,早期前端只负责写页面,然后把写好的HTML页面给后端,后端使用模板引擎(Jsp,Thymeleaf、 freemarker)进行开发。
前后端分离的好处:各自开发,相对独立,松耦合,前后端通过API进行交互,后端提供接口给前端,前端去调用该接口,但可能会导致前后端团队人员不能做到及时协商,出现一些问题。解决方式:早期使用实时更新文档,但非常繁琐,后来又使用postman来进行一些测试。
swagger是一个Api接口文档,官网:https://swagger.io/
14.2导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
14.3 配置application.properties
#springboot2.60版本需要更改springmvc路径匹配规则
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
14.4创建配置类
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
}
然后启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:
14.5手动配置实例,修改SwaggerConfig配置类
package com.qf.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API接口文档标题")
.description("API接口文档描述")
.termsOfServiceUrl("http://www.baidu.com")
.version("1.0")
.build();
}
}
再次启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:
14.6创建实体类
package com.qf.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户对象")
public class User {
@ApiModelProperty("编号")
private Integer id;
@ApiModelProperty("姓名")
private String name;
}
14.7创建controller
package com.qf.controller;
import com.qf.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("user")
@Api(tags = "用户接口")
public class UserController {
@PostMapping("addUser")
@ApiOperation("添加用户")
public String addUser(){
return "success";
}
@DeleteMapping("deleteUser")
@ApiOperation("删除用户")
public String deleteUser(@RequestParam @ApiParam("用户编号") String id){
System.out.println("deleteUser:"+id);
return "success";
}
@PutMapping("updateUser")
@ApiOperation("修改用户")
public User updateUser(){
System.out.println("updateUser");
User user = new User(1001,"张三");
return user;
}
@GetMapping("findById")
@ApiOperation("查询单个用户")
public User findById(@RequestParam @ApiParam("用户编号")String id){
System.out.println("findById");
User user = new User(Integer.parseInt(id),"李四");
return user;
}
@RequestMapping("findAll")
@ApiOperation("查询用户")
public List<User> findAll(){
System.out.println("findAll");
return Arrays.asList(new User(1003,"王五"),new User(1004,"赵六"));
}
}
14.8修改SwaggerConfig配置类
package com.qf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(createApiInfo())
.groupName("yangl")////分组名称(可以创建多个Docket就有多个组名)
.enable(true)//enable表示是否开启Swagger
.select()
//RequestHandlerSelectors指定扫描的包
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
.build();
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
public ApiInfo createApiInfo(){
return new ApiInfoBuilder()
.title("测试标题")
.description("测试描述")
.termsOfServiceUrl("http://www.baidu.com")
.build();
//return ApiInfo.DEFAULT;
}
}
Swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiModel:用对象来接收参数 ,修饰类
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述,一般描述错误的响应
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiParam:单个参数描述
@ApiImplicitParam:一个请求参数,用在方法上
@ApiImplicitParams:多个请求参数
十五、SpringBoot整合knife4j
15.1 knife4j介绍
knife4j可以理解swagger的升级版,采用的是后端Java代码和Ui都混合在一个Jar包里面的方式提供给开发者使用,Knife4j不仅仅将前身的Ui皮肤通过Vue技术栈进行了重写,也增加了更多个性化的特性增强功能,基于springfox项目以及OpenAPI的规范,目前主要支持以Java开发为主,并且是依赖于大环境下使用的
Spring MVC
、Spring Boot
、Spring Cloud
框架.
15.2 导入依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
15.3创建配置类
官网代码拷贝过来之后,注意修改扫描对应的包名:RequestHandlerSelectors.basePackage("com.qf.controller")
package com.qf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
//.title("swagger-bootstrap-ui-demo RESTful APIs")
.description("# swagger-bootstrap-ui-demo RESTful APIs")
.termsOfServiceUrl("http://www.baidu.com/")
.contact(new Contact("张三","www.zhangs.com","zhangs@qq.com"))
.version("1.0")
.build())
//分组名称
.groupName("2.X版本")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
//.paths(PathSelectors.any())
.build();
return docket;
}
}
15.4 创建controller
package com.qf.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
@Api(tags = "首页模块")
@RestController
public class IndexController {
@ApiImplicitParam(name = "name",value = "姓名",required = true)//参数说明
@ApiOperation(value = "向客人问好")
@GetMapping("/sayHi")
public ResponseEntity<String> sayHi(@RequestParam(value = "name")String name){
return ResponseEntity.ok("Hi:"+name);//ResponseEntity返回对应数据
}
}