SpringBoot 核心功能讲解
SpringBoot之web请求静态资源
我们可以在浏览器访问src/main/resources/static目录下的静态资源,在此目录下新建test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态资源文件</title>
</head>
<body>
<div id="test">
Hello SpringBoot~
</div>
</body>
</html>
运行项目,浏览器访问http://localhost:8090/test.html
也可以访问之前放的图片
我们也可以在application.yml中定义更多层级的目录的
spring:
banner:
location: classpath:static/banner.txt
mvc:
static-path-pattern: /abc/**
我们先改回去,然后在src/main/resources/static目录下创建index.html,该文件为默认首页,访问时不需要文件名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<div id="test">
Hello Index~
</div>
</body>
</html>
然后启动项目,浏览器直接访问http://localhost:8090/
在src/main/resources/static引入文件名为favicon.ico的图标,maven install项目之后运行项目,浏览器再访问的时候浏览器tab上已经有了改图标
通过Lombok提高开发效率以及日志设置
首先打开pom.xml文件引入Lombok依赖
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
然后在IDEA通过点击File=>Settings开发设置面板,找到Plugins,然后搜索Lombok安装(本人IDEA是2022版,发现IDEA默认装过了)
然后在com.ql.springbootlearn.pojo包下创建Student.java
package com.ql.springbootlearn.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Student {
public String name;
public Integer age;
}
其中@Data生成get和set方法,@ToString生成toString方法,@NoArgsConstructor生成默认构造方法,@AllArgsConstructor生成全参构造方法。
然后在HelloController.java中调用
@RestController
@Slf4j
public class HelloController {
...
@GetMapping("getStudent")
public Object getStudent(){
Student student = new Student();
student.setName("Smith");
student.setAge(28);
Student student1 = new Student("Jams", 34);
log.debug(student.toString());
log.info(student.toString());
log.warn(student.toString());
log.error(student.toString());
return student;
}
}
其中@Slf4j也是Lombok的注解,注入日志组件
运行项目,浏览器访问http://localhost:8090/getStudent
因为SpringBoot项目默认日志级别是info,所以debug日志没显式。
注意:当然Lombok也有缺点,在团队开发的时候,只要有一个人使用,所有人必须使用Lombok,不然项目无法编译通过。如果是个人的话,完全没有问题。
Restful 接口请求风格
GET 主要用于查询类的操作。
POST 主要用于Form表单提交或相对安全的一些请求。主要保存新的数据。
PUT 主要用于数据的修改。
DELETE 用户数据的删除。
有些公司强制要求用四种请求。另外有些公司使用的是弱规范,就是查询使用GET,增删改使用POST。
在com.ql.springbootlearn.controller包下创建StuController.java
package com.ql.springbootlearn.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/stu")
public class StuController {
@GetMapping("get")
public String getStu(){
return "查询Stu";
}
@PostMapping("create")
public String crrateStu(){
return "新增Stu";
}
@PutMapping("update")
public String updateStu(){
return "修改Stu";
}
@DeleteMapping("delete")
public String deleteStu(){
return "删除Stu";
}
}
运行项目,打开postman测试
SpringBoot 之接受参数的常用注解
@RequestParam 用于获得URL中的请求参数,如果参数变量名保持一致,该注解可以省略。
@PathVarible 用于获得路径的参数
@RequestBody 用于获得请求的body
@RequestHeader 用于获得请求头中的一些参数
@CookieValue 用于获得Cookie中一些参数
打开StuController.java修改getStu方法
@GetMapping("{stuId}/get")
public String getStu(@PathVariable("stuId") String stuId,
@RequestParam Integer id,
@RequestParam String name){
log.info("stuId="+ stuId);
log.info("id="+ id);
log.info("name="+ name);
return "查询Stu";
}
运行项目,打开postman请求测试
打开StuController.java修改crrateStu方法
@PostMapping("create")
public String crrateStu(@RequestBody Map<String, Object> map,
@RequestHeader("token") String token,
@CookieValue("clientId") String clientId,
HttpServletRequest request){
log.info("token="+token);
log.info("clientId="+clientId);
log.info("map="+map.toString());
String requestHeader = request.getHeader("token");
log.info("requestHeader="+requestHeader);
return "新增Stu";
}
运行项目,打开postman请求测试
SpringBoot 之接口返回响应对象
在com.ql.springbootlearn.utils包下新增接口返回响应JSONResult工具类
package com.ql.springbootlearn.utils;
/**
* 自定义响应数据结构
* 本类可提供给 H5/ios/安卓/公众号/小程序 使用
* 前端接受此类数据(json object)后,可自行根据业务去实现相关功能
* 200: 表示成功
* 500: 表示错误,错误信息在msg字段中
*/
public class JSONResult {
private Integer status;//响应业务状态
private String msg;//响应消息
private Object data;//响应中的数据
public static JSONResult build(Integer status, String msg, Object data){
return new JSONResult(status, msg, data);
}
public static JSONResult ok(Object data){
return new JSONResult(data);
}
public static JSONResult ok(){
return new JSONResult(null);
}
public static JSONResult errorMsg(String msg){
return new JSONResult(500, msg, null);
}
public JSONResult() {
}
public JSONResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public JSONResult(Object data){
this.status = 200;
this.msg = "OK";
this.data = data;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
然后修改HelloController.java的getStudent方法测试
@GetMapping("getStudent")
public JSONResult getStudent(){
Student student = new Student();
student.setName("Smith");
student.setAge(28);
Student student1 = new Student("Jams", 34);
log.debug(student.toString());
log.info(student.toString());
log.warn(student.toString());
log.error(student.toString());
return JSONResult.errorMsg("调用接口出现异常");
//return JSONResult.ok(stu);
}
@GetMapping("getStudent")
public JSONResult getStudent(){
Student student = new Student();
student.setName("Smith");
student.setAge(28);
Student student1 = new Student("Jams", 34);
log.debug(student.toString());
log.info(student.toString());
log.warn(student.toString());
log.error(student.toString());
//return JSONResult.errorMsg("调用接口出现异常");
return JSONResult.ok(stu);
}
SpringBoot实现文件上传
打开src/main/resources/static/test.html文件,添加文件上传的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态资源文件</title>
</head>
<body>
<div id="test">
Hello SpringBoot~
</div>
<!--文件上传-->
<form action="/upload" method="post" enctype="multipart/form-data">
<div>
<input type="file" id="file" name="file" value="选择图片"
accept="image/jpeg,image/jpg,image/png">
</div>
<div>
<input type="submit" value="上传文件">
</div>
</form>
</body>
</html>
打开HelloController.java添加文件上传的方法
@PostMapping("upload")
public String upload(MultipartFile file) throws Exception{
file.transferTo(new File("D:/upload/"+file.getOriginalFilename()));
return "上传成功";
}
运行项目,浏览器访问http://localhost:8090/test.html上传文件
SpringBoot 之自定义异常页面
我们在application.yml中添加文件上传的相关配置
spring:
servlet:
multipart:
max-file-size: 200KB # 文件上传大小的限制,设置最大值,不能超过,否则报错
max-request-size: 2MB # 文件最大请求限制,用于批量
启动项目,浏览器访问http://localhost:8090/test.html上传一个大于200KB的图片
现在报异常之后,页面展示的不友好,所以我们需要自定义一个异常页面。
首先,打开pom.xml文件,引入thymeleaf模板引擎
<!--SpringBoot官方推荐的模板thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
然后在src/main/resources/templates目录下创建error.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态资源文件</title>
</head>
<body>
<div id="test" style="color: red">
系统发生异常,请联系管理员!
</div>
</body>
</html>
重新运行项目,再上传刚才的图片,返回的是我们写好的页面。
SpringBoot 之统一异常封装处理
如果是前后端分离的项目,出现异常就不能返回页面了,应该把异常信息返回去。
在com.ql.springbootlearn包下创建一个GraceExceptionHandler.java统一异常拦截处理类
package com.ql.springbootlearn;
import com.ql.springbootlearn.utils.JSONResult;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 统一异常拦截处理
* 可以针对异常自定义去处理去捕获,返回指定的类型(json类型)到前端
*/
@ControllerAdvice
public class GraceExceptionHandler {
@ExceptionHandler(FileSizeLimitExceededException.class)
@ResponseBody
public JSONResult returnMaxFileSizeLimit(FileSizeLimitExceededException e){
return JSONResult.errorMsg("文件大小不能超过200KB");
}
}
运行项目,再上传刚才的图片测试。
SpringBoot 实现拦截器
在com.ql.springbootlearn.controller.interceptor包下创建拦截器UserInfoInterceptor.java
package com.ql.springbootlearn.controller.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
/**
* 拦截请求,访问Controller之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
String userToken = request.getHeader("userToken");
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(userToken)){
log.error("用户校验不通过,信息不完整!");
return false;
}
//假设真实的用户id是1001,用户token是abcxyz
if(!userId.equalsIgnoreCase("1001") ||
!userToken.equalsIgnoreCase("abcxyz")){
log.error("用户权限不通过!");
return false;
}
/**
* false: 请求被拦截
* true: 请求放行,可以继续访问后面的Controller
*/
return true;
}
/**
* 请求访问到Controller之后,渲染视图之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 请求访问到Controller之后,渲染视图之后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
然后注册拦截器,在com.ql.springbootlearn包下创建InterceptorConfig.java拦截器配置类
package com.ql.springbootlearn;
import com.ql.springbootlearn.controller.interceptor.UserInfoInterceptor;
import org.springframework.context.annotation.Bean;
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 InterceptorConfig implements WebMvcConfigurer {
@Bean
public UserInfoInterceptor userInfoInterceptor(){
return new UserInfoInterceptor();
}
public void addInterceptors(InterceptorRegistry registry){
//注册拦截器
registry.addInterceptor(userInfoInterceptor())
.addPathPatterns("/upload");
}
}
运行项目,打开postman测试
当用户id和token为空时
当用户id和token错误时
当所有信息正确时,上传成功。
自定义异常与拦截器整合返回JSON对象
首先,在com.ql.springbootlearn.exception包下创建一个自定义异常类MyCustomException
package com.ql.springbootlearn.exception;
/**
* 自定义异常
* 目的:
* 统一处理异常信息
* 便于解耦,可以在拦截器,控制层,业务层去使用
*/
public class MyCustomException extends RuntimeException{
public MyCustomException(String errorMsg){
super(errorMsg);
}
}
然后在GraceExceptionHandler.java中捕获该异常
package com.ql.springbootlearn.exception;
import com.ql.springbootlearn.utils.JSONResult;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 统一异常拦截处理
* 可以针对异常自定义去处理去捕获,返回指定的类型(json类型)到前端
*/
@ControllerAdvice
public class GraceExceptionHandler {
@ExceptionHandler(FileSizeLimitExceededException.class)
@ResponseBody
public JSONResult returnMaxFileSizeLimit(FileSizeLimitExceededException e){
return JSONResult.errorMsg("文件大小不能超过200KB");
}
@ExceptionHandler(MyCustomException.class)
@ResponseBody
public JSONResult returnMyCustomException(MyCustomException e){
return JSONResult.errorMsg(e.getMessage());
}
}
然后在com.ql.springbootlearn.exception写一个抛出异常的调用类GraceException.java
package com.ql.springbootlearn.exception;
/**
* 优雅的处理异常,进行调用
*/
public class GraceException {
public static void display(String errMsg) {
throw new MyCustomException(errMsg);
}
}
然后,在前面的用户信息拦截器UserInfoInterceptor.java里调用抛出异常
package com.ql.springbootlearn.controller.interceptor;
import com.ql.springbootlearn.exception.GraceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
/**
* 拦截请求,访问Controller之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
String userToken = request.getHeader("userToken");
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(userToken)){
log.error("用户校验不通过,信息不完整!");
GraceException.display("用户校验不通过,信息不完整!");
return false;
}
//假设真实的用户id是1001,用户token是abcxyz
if(!userId.equalsIgnoreCase("1001") ||
!userToken.equalsIgnoreCase("abcxyz")){
log.error("用户权限不通过!");
GraceException.display("用户权限不通过!");
return false;
}
/**
* false: 请求被拦截
* true: 请求放行,可以继续访问后面的Controller
*/
return true;
}
/**
* 请求访问到Controller之后,渲染视图之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 请求访问到Controller之后,渲染视图之后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
再运行项目,打开postman测试
SpringBoot定时任务的实现
在com.ql.springbootlearn.utils包下创建MyTask定时任务演示类
package com.ql.springbootlearn.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDateTime;
@Configuration //1.标记配置类,使得springboot容器可以扫描到
@EnableScheduling //2.开启定时任务
@Slf4j
public class MyTask {
//3.添加一个任务,并且注明任务的运行表达式
@Scheduled(cron = "*/5 * * * * ?")
public void publishMsg(){
log.warn("开始执行任务:"+ LocalDateTime.now());
}
}
运行项目,查看控制台。
注意:当前定时任务为Spring自带的,只适合单体环境运行,如果是一个集群或者分布式环境就不适合该方案了。
SpringBoot异步任务的实现
在com.ql.springbootlearn.utils包下创建异步任务类
package com.ql.springbootlearn.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@EnableAsync
@Slf4j
public class MyAsyncTask {
@Async
public void publishMsg(){
try {
Thread.sleep(5000);
log.warn("异步任务处理完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然后在HelloController.java里调用
...
@Autowired
private MyAsyncTask myAsyncTask;
@GetMapping("getMyConfig")
public Object getMyConfig(){
myAsyncTask.publishMsg();
log.info("这是跳过异步任务的执行");
return myConfig;
}
运行项目,浏览器访问http://localhost:8090/getMyConfig,查看控制台
注意:该方案适合单应用,若是分布式环境一般通过消息队列去处理。