springboot

springboot

本文是根据其他文章(搜索springboot,榜一)自己做的笔记以便日后查看
部分内容还没接触过暂且跳过
参考文章链接
这篇文章写得不错,但是排版不是特别友好。正好自己先整理一部分也当复习了
其余部分之后会进行不定时更新

一、初级部分

1. 环境搭建

  1. IDEA 中可以通过File->New->Project,选择 Spring Initializr
  2. 创建了 Spring Boot 项目之后,需要进行 maven 配置。打开File->settings,搜索 maven,配置一下本地的 maven 信息

在这里插入图片描述

启动类 xxApplication,该类上面有个@SpringBootApplication注解

到此为止,Spring Boot 就启动成功了,为了比较清楚的看到效果,我们写一个 Controller 来测试一下,如下:
在这里插入图片描述

访问刚才的请求路径

在这里插入图片描述
Spring Boot 端口号默认是8080,如果想要修改,可以在 application.yml 文件中使用 server.port 来人为指定端口
在这里插入图片描述


2. Springboot返回json数据及数据封装

  • 在 Controller 中使用@RestController注解即可返回 Json 格式的数据
  • @RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解
  • Spring Boot 中默认使用的 Json 解析技术框架是 jackson
2.1 Spring Boot 默认对Json的处理

在实际项目中,常用的数据结构无非有类对象、List对象、Map对象,我们看一下默认的 jackson 框架对这三个常用的数据结构转成 json 后的格式如何。

  1. 首先创建一个实体类

在这里插入图片描述

  1. 三种类型的返回值对应的json格式

在这里插入图片描述

对象:
在这里插入图片描述

List:
在这里插入图片描述
Map:
在这里插入图片描述

可以看出,map 中不管是什么数据类型,都可以转成相应的 json 格式


2.2 使用阿里巴巴FastJson的设置

从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑

2.2.1 导入fastjson坐标

在这里插入图片描述

2.2.2 封装统一返回的数据结构

在实际项目中,除了要封装数据之外,往往需要在返回的 json 中添加一些其他信息,比如状态码 code ,msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的 json 返回结构存储返回信息。

  1. 定义统一的 json 结构

由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下:

package com.lzh.boot.config;

public class JsonResult<T> {

    private T data;
    public String code;
    private String msg;

    //若没有数据返回,默认状态码为0,提示信息为:操作成功!
    public JsonResult() {
        this.code="0";
        this.msg="操作成功";
    }

    //若没有数据返回,可以人为指定状态码和提示信息
    public JsonResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    //有数据返回时,状态码为0,默认提示信息为:操作成功!
    public JsonResult(T data) {
        this.data = data;
        this.code="0";
        this.msg="操作成功";
    }

    //有数据返回,状态码为0,人为指定提示信息
    public JsonResult(T data, String msg) {
        this.data = data;
        this.code="0";
        this.msg = msg;
    }
}
  1. 修改controller返回值类型

由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型

在这里插入图片描述

结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


3. Spring Boot使用slf4j进行日志记录

SLF4J,即简单日志门面,不是具体的日志解决方案,它只服务于各种各样的日志系统。

你只需要按统一的方式写记录日志的代码,而无需关心日志是通过哪个日志系统,以什么风格输出的。因为它们取决于部署项目时绑定的日志系统。例如,在项目中使用了 slf4j 记录日志,并且绑定了 log4j(即导入相应的依赖),则日志会以 log4j 的风格输出;后期需要改为以 logback 的风格输出日志,只需要将 log4j 替换成 logback 即可,不用修改项目中的代码。这对于第三方组件的引入的不同日志系统来说几乎零学习成本,况且它的优点不仅仅这一个而已,还有简洁的占位符的使用和日志级别的判断。

应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

使用lombok中的@Slf4j注解输出日志:log.info/success/erroe()
在这里插入图片描述


4. Spring Boot中的项目属性配置

4.1 少量配置信息的情形

举个例子,在微服务架构中,最常见的就是某个服务需要调用其他服务来获取其提供的相关信息,那么在该服务的配置文件中需要配置被调用的服务地址,比如在当前服务里,我们需要调用订单微服务获取订单相关的信息,假设 订单服务的端口号是 8002,那我们可以做如下配置:

在这里插入图片描述

在业务代码中如何获取到这个配置的订单服务地址呢?我们可以使用@Value注解来解决。在对应的类中加上一个属性,在属性上使用 @Value 注解即可获取到配置文件中的配置信息,如下:

@Value 注解上通过${key}即可获取配置文件中和 key 对应的 value 值

在这里插入图片描述

结果
在这里插入图片描述


4.2 多个配置信息的情形

随着业务复杂度的增加,一个项目中可能会有越来越多的微服务,某个模块可能需要调用多个微服务获取不同的信息,那么就需要在配置文件中配置多个微服务的地址。可是,在需要调用这些微服务的代码中,如果这样一个个去使用 @Value 注解引入相应的微服务地址的话,太过于繁琐,也不科学。

所以,在实际项目中,业务繁琐,逻辑复杂的情况下,需要考虑封装一个或多个配置类。举个例子:假如在当前服务中,某个业务需要同时调用订单微服务、用户微服务和购物车微服务,分别获取订单、用户和购物车相关信息,然后对这些信息做一定的逻辑处理。那么在配置文件中,我们需要将这些微服务的地址都配置好:

在这里插入图片描述

实际业务中,远远不止这三个微服务,甚至十几个都有可能。对于这种情况,我们可以先定义一个配置类来专门保存微服务的 url,如下:

还可以在配置类中定义方法,使用@Bean注解将方法的返回值注入的容器中,如获取数据源连接

在这里插入图片描述

使用 @ConfigurationProperties 注解并且使用 prefix 来指定一个前缀,然后该类中的属性名就是配置中去掉前缀后的名字,一一对应即可。即:前缀名 + 属性名就是配置文件中定义的 key。

使用 @ConfigurationProperties 注解会有警告,导入需要的依赖即可

写个 Controller 来测试一下。此时,不需要在代码中一个个引入这些微服务的 url 了,直接通过 @Autowire注解将刚刚写好配置类注入进来即可使用了,非常方便。如下:
在这里插入图片描述
在这里插入图片描述

4.3 指定项目配置文件

在实际项目中,一般有两个环境:开发环境和生产环境。开发环境中的配置和生产环境中的配置往往不同
最好的解决方法就是开发环境和生产环境都有一套对用的配置信息,然后当我们在开发时,指定读取开发环境的配置,当我们将项目部署到服务器上之后,再指定去读取生产环境的配置。

在这里插入图片描述

这样只有使用被指定的yml文件中的8081端口号才能访问成功
在这里插入图片描述


5. Spring Boot中的MVC支持

Spring Boot 的 MVC 支持主要来介绍实际项目中最常用的几个注解,包括 @RestController、 @RequestMapping、@PathVariable、@RequestParam 以及 @RequestBody。主要介绍这几个注解常用的使用方式和特点。

5.1 @RestController

@RestController 可以看作是 @Controller 和 @ResponseBody 的结合体

5.2 @RequestMapping

@RequestMapping 是一个用来处理请求地址映射的注解,它可以用于类上,也可以用于方法上。在类的级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上,表示类中的所有响应请求的方法都是以该地址作为父路径;在方法的级别表示进一步指定到处理方法的映射关系。

  • value 属性:指定请求的实际地址,value 可以省略不写
  • method 属性:指定请求的类型,主要有 GET、PUT、POST、DELETE,默认为 GET
  • produces属性:指定返回内容类型,如 produces = “application/json; charset=UTF-8”

针对四种不同的请求方式,是有相应注解的,不用每次在 @RequestMapping 注解中加 method 属性来指定,上面的 GET 方式请求可以直接使用 @GetMapping(“/get”) 注解,效果一样。相应地,PUT 方式、POST 方式和 DELETE 方式对应的注解分别为 @PutMapping、@PostMapping 和 DeleteMapping。

5.3 @PathVariable

@PathVariable 注解主要是用来获取 url 参数,Spring Boot 支持 restfull 风格的 url,比如一个 GET 请求携带一个参数 id 过来,我们将 id 作为参数接收,可以使用 @PathVariable 注解。如下:

@GetMapping("/user/{id}")
public String testPathVariable(@PathVariable Integer id) {
	System.out.println("获取到的id为:" + id);
	return "success";
}

需要注意一个问题,如果想要 url 中占位符中的 id 值直接赋值到参数 id 中,需要保证 url 中的参数和方法接收参数一致,否则就无法接收。如果不一致的话,其实也可以解决,需要用 @PathVariable 中的 value 属性来指定对应关系。如下:

@RequestMapping("/user/{idd}")
public String testPathVariable(@PathVariable(value = "idd") Integer id) {
	System.out.println("获取到的id为:" + id);
	return "success";
}

对于访问的 url,占位符的位置可以在任何位置,不一定非要在最后,比如这样也行:/xxx/{id}/user。另外,url 也支持多个占位符,方法参数使用同样数量的参数来接收,原理和一个参数是一样的,例如:

@GetMapping("/user/{idd}/{name}")
    public String testPathVariable(@PathVariable(value = "idd") Integer id, @PathVariable String name) {
        System.out.println("获取到的id为:" + id);
        System.out.println("获取到的name为:" + name);
        return "success";
    }
5.4 @RequestParam

@RequestParam 注解顾名思义,也是获取请求参数的,上面我们介绍了 @PathValiable 注解也是获取请求参数的,那么 @RequestParam 和 @PathVariable 有什么不同呢?主要区别在于: @PathValiable 是从 url 模板中获取参数值, 即这种风格的 url:http://localhost:8080/user/{id} ;而 @RequestParam 是从 request 里面获取参数值,即这种风格的 url:http://localhost:8080/user?id=1

@GetMapping("/user")
public String testRequestParam(@RequestParam Integer id) {
	System.out.println("获取到的id为:" + id);
	return "success";
}

同样地,url 上面的参数和方法的参数需要一致,如果不一致,也需要使用 value 属性来说明,比如 url 为:http://localhost:8080/user?idd=1

@RequestMapping("/user")
public String testRequestParam(@RequestParam(value = "idd", required = false) Integer id) {
	System.out.println("获取到的id为:" + id);
	return "success";
}

除了 value 属性外,还有个两个属性比较常用:

required 属性:true 表示该参数必须要传,否则就会报 404 错误,false 表示可有可无。
defaultValue 属性:默认值,表示如果请求中没有同名参数时的默认值。
从 url 中可以看出,@RequestParam 注解用于 GET 请求上时,接收拼接在 url 中的参数。除此之外,该注解还可以用于 POST 请求,接收前端表单提交的参数,假如前端通过表单提交 username 和 password 两个参数,那我们可以使用 @RequestParam 来接收,用法和上面一样。

@PostMapping("/form1")
    public String testForm(@RequestParam String username, @RequestParam String password) {
        System.out.println("获取到的username为:" + username);
        System.out.println("获取到的password为:" + password);
        return "success";
    }

那么问题来了,如果表单数据很多,我们不可能在后台方法中写上很多参数,每个参数还要 @RequestParam 注解。针对这种情况,我们需要封装一个实体类来接收这些参数,实体中的属性名和表单中的参数名一致即可。

public class User {
	private String username;
	private String password;
	// set get
}

使用实体接收的话,我们不能在前面加 @RequestParam 注解了,直接使用即可。

@PostMapping("/form2")
    public String testForm(User user) {
        System.out.println("获取到的username为:" + user.getUsername());
        System.out.println("获取到的password为:" + user.getPassword());
        return "success";
    }
5.5 @RequestBody

@RequestBody 注解用于接收前端传来的实体,接收参数也是对应的实体,比如前端通过 json 提交传来两个参数 username 和 password,此时我们需要在后端封装一个实体来接收。在传递的参数比较多的情况下,使用 @RequestBody 接收会非常方便。例如:

public class User {
	private String username;
	private String password;
	// set get
}
@PostMapping("/user")
public String testRequestBody(@RequestBody User user) {
	System.out.println("获取到的username为:" + user.getUsername());
	System.out.println("获取到的password为:" + user.getPassword());
	return "success";
}

在这里插入图片描述

可以看出,@RequestBody 注解用于 POST 请求上,接收 json 实体参数。它和上面我们介绍的表单提交有点类似,只不过参数的格式不同,一个是 json 实体,一个是表单提交。在实际项目中根据具体场景和需要使用对应的注解即可。


6. Spring Boot集成 Swagger2 展现在线接口文档

7. Spring Boot集成Thymeleaf模板引擎

8. Spring Boot中的全局异常处理

我们不希望直接把异常抛给用户,应该对异常进行处理,对错误信息进行封装,然后返回一个友好的信息给用户

8.1 定义返回的统一 json 结构

方便结合全局异常处理信息,因为异常处理信息中一般我们需要把状态码和异常内容反馈给调用方。

在这里插入图片描述

8.2 处理系统异常

需要新建一个 GlobalExceptionHandler 全局异常处理类,然后加上 @ControllerAdvice 注解即可拦截项目中抛出的异常,如下:
在这里插入图片描述

  • @ControllerAdvice 注解包含了 @Component 注解,交给 Spring 来管理
  • 该注解有 basePackages 属性,用来拦截哪个包中的异常信息,一般不指定这个属性,拦截所有异常
  • @ResponseBody 注解是为了异常处理完之后给调用方输出一个 json 格式的封装数据
  • 在此类的方法上通过·@ExceptionHandler 注解来指定具体的异常,然后在方法中处理该异常信息,最后将结果通过统一的 json 结构体返回给调用者
8.2.1 处理参数缺失异常

前后端分离的架构中,前端请求后台的接口都是通过 rest 风格来调用,有时候,比如 POST 请求 需要携带一些参数,但是往往有时候参数会漏掉。另外,在微服务架构中,涉及到多个微服务之间的接口调用时,也可能出现这种情况,此时我们需要定义一个处理参数缺失异常的方法,来给前端或者调用方提示一个友好信息。

/**
* 缺少请求参数异常
* @param ex HttpMessageNotReadableException
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public JsonResult handleHttpMessageNotReadableException(
    MissingServletRequestParameterException ex) {
    logger.error("缺少请求参数,{}", ex.getMessage());
    return new JsonResult("400", "缺少必要的请求参数");
}
@RestController
@RequestMapping("/exception")
public class ExceptionController {
 
    private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class);
 
    @PostMapping("/test")
    public JsonResult test(@RequestParam("name") String name,
                           @RequestParam("pass") String pass) {
        logger.info("name:{}", name);
        logger.info("pass:{}", pass);
        return new JsonResult();
    }
}

在这里插入图片描述

8.3 拦截自定义异常

由于在业务中,有很多异常,针对不同的业务,可能给出的提示信息不同,所以为了方便项目异常信息管理,我们一般会定义一个异常信息枚举类。比如:

/**
 * 业务异常提示信息枚举类
 * @author shengwu ni
 */
public enum BusinessMsgEnum {
    /** 参数异常 */
    PARMETER_EXCEPTION("102", "参数异常!"),
    /** 等待超时 */
    SERVICE_TIME_OUT("103", "服务调用超时!"),
    /** 参数过大 */
    PARMETER_BIG_EXCEPTION("102", "输入的图片数量不能超过50张!"),
    /** 500 : 一劳永逸的提示也可以在这定义 */
    UNEXPECTED_EXCEPTION("500", "系统发生异常,请联系管理员!");
    // 还可以定义更多的业务异常
 
    /**
     * 消息码
     */
    private String code;
    /**
     * 消息内容
     */
    private String msg;
 
    private BusinessMsgEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
	// set get方法
}

然后我们可以定义一个业务异常,当出现业务异常时,我们就抛这个自定义的业务异常即可。比如我们定义一个 BusinessErrorException 异常,如下:

/**
 * 自定义业务异常
 * @author shengwu ni
 */
public class BusinessErrorException extends RuntimeException {
    
    private static final long serialVersionUID = -7480022450501760611L;
 
    /**
     * 异常码
     */
    private String code;
    /**
     * 异常提示信息
     */
    private String message;
 
    public BusinessErrorException(BusinessMsgEnum businessMsgEnum) {
        this.code = businessMsgEnum.code();
        this.message = businessMsgEnum.msg();
    }
	// get set方法
}

在构造方法中,传入我们上面自定义的异常枚举类,所以在项目中,如果有新的异常信息需要添加,我们直接在枚举类中添加即可,很方便,做到统一维护,然后再拦截该异常时获取即可。

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
 
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 拦截业务异常,返回业务异常信息
     * @param ex
     * @return
     */
    @ExceptionHandler(BusinessErrorException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult handleBusinessError(BusinessErrorException ex) {
        String code = ex.getCode();
        String message = ex.getMessage();
        return new JsonResult(code, message);
    }
}

在业务代码中,我们可以直接模拟一下抛出业务异常,测试一下:

@RestController
@RequestMapping("/exception")
public class ExceptionController {
 
    private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class);
 
    @GetMapping("/business")
    public JsonResult testException() {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            throw new BusinessErrorException(BusinessMsgEnum.UNEXPECTED_EXCEPTION);
        }
        return new JsonResult();
    }
}
{"code":"500","msg":"系统发生异常,请联系管理员!"}

本节课程主要讲解了Spring Boot 的全局异常处理,包括异常信息的封装、异常信息的捕获和处理,以及在实际项目中,我们用到的自定义异常枚举类和业务异常的捕获与处理,在项目中运用的非常广泛,基本上每个项目中都需要做全局异常处理。

13. Spring Boot中使用拦截器

使用拦截器很简单,只需要两步即可:定义拦截器和配置拦截器

13.1 定义拦截器

定义拦截器,只需要实现 HandlerInterceptor 接口,HandlerInterceptor 接口是所有自定义拦截器或者 Spring Boot 提供的拦截器的鼻祖,所以,首先来了解下该接口。该接口中有三个方法: preHandle、postHandle 和 afterCompletion

preHandle 方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某个方法,且在这个方法执行之前。所以 preHandle(……) 方法可以决定是否将请求放行,这是通过返回值来决定的,返回 true 则放行,返回 false 则不会向后执行。

postHandle方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某个方法,且在执行完了该方法,但是在 DispatcherServlet 视图渲染之前。所以在这个方法中有个 ModelAndView 参数,可以在此做一些修改动作。

afterCompletion 方法:顾名思义,该方法是在整个请求处理完成后(包括视图渲染)执行,这时做一些资源的清理工作,这个方法只有在 preHandle(……) 被成功执行后并且返回 true 才会被执行。

/**
 * 自定义拦截器
 * @author shengwu ni
 * @date 2018/08/03
 */
public class MyInterceptor implements HandlerInterceptor {
 
    private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        String methodName = method.getName();
        logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName);
        // 返回true才会继续执行,返回false则取消当前请求
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染");
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了");
    }
}

13.2 配置拦截器

直接实现 WebMvcConfigurer 接口,然后重写 addInterceptors 方法,将自定义的拦截器添加进去即可,如下:

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 实现WebMvcConfigurer不会导致静态资源被拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}

实现 WebMvcConfigure 接口的话,不会拦截 Spring Boot 默认的静态资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LJFCZH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值