当前位置: 首页 > news >正文

统一功能处理

SpringBoot统一功能处理

本节目表
1.掌握拦截器的使用,及其原理
2.学习统一数据返回格式和统一异常处理的操作
3.了解一些Spirng的源码


文章目录

  • SpringBoot统一功能处理
  • 前言
  • 一、拦截器是什么?
  • 二、使用步骤
    • 1.定义拦截器
    • 2.注册配置拦截器
  • 三,适配器
  • 四丶统一数据返回格式
  • 五、统一异常处理
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:
前面我们实现了强制登录的功能,后端程序根据Session来判断用户是否登录,但是实现方法是比较麻烦的

需要修改每个接口的处理逻辑
需要修改每个接口的返回结果
接口定义修改,前段代码也需要跟着修改


提示:以下是本篇文章正文内容,下面案例可供参考

一、拦截器是什么?

拦截器是Spring框架提供的核心功能之一,主要用来拦截用户的请求,在指定方法(接口)前后,根据业务需要执行预先设定的代码

也就是说,允许开发人员提前预定以一些逻辑,在用户请求响应前后执行,也可以在用户请求前阻止其执行
在拦截器当中,开发人员可以在应用程序中做一些通用性的操作,比如通过拦截器来拦截前端发来的请求,判断Session中是否有登录用户信息,如果有就可以放心,如果没有就进行拦截

在这里插入图片描述

生活中实际的例子: 比如我们去银行办理业务,在办理业务前后,就可以加一些拦截操作 办理业务之前,先取号,如果带了身份证了就取号成功
业务办理结束,给业务办理人员进行评价 这就是"拦截器"做到工作

二、使用步骤

拦截器使用步骤有两步

  1. 定义拦截器 2. 注册拦截器

1.定义拦截器

自定义拦截器:实现HandlerInterceptor接口, 并重写其所有方法

代码如下(示例):登录拦截器

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    //定义拦截器   实现父类的所有方法

    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       log.info("登录拦截...");
       //true 放行 false  拦截
        //判断用户是否登录 判空和有值
        HttpSession session = request.getSession();
        if(session.getAttribute(Constants.SESSION_USER_KEY)==null||
                !StringUtils.hasLength((String) session.getAttribute(Constants.SESSION_USER_KEY))){
            //未登录进行拦截 ,需要设置一些响应 返回给客服端
            log.error("用户未登录,进行拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());//设置状态码,没权限401
            response.setContentType("application/json,charset=utf-8");//设置返回数据格式为json
//            response.getOutputStream().write("{\"ok\" : false}".getBytes());//设置返回的内容
            Result result = Result.unlogin();//登录失败,返回统一格式
            response.getOutputStream().write(objectMapper.writeValueAsString(result).getBytes());
            return false;
        }
        log.info("用户登录校验通过");
        return true;
    }

代码说明

创建了一个拦截器LoginInterceptor类 ,实现了HandlerInterceptor接口,使用接口中的preHandle()方法,返回值为bool类型 true 为放行,false 为拦截 ,当前拦截器是判断是否存在登录的Session,有就登录成功,执行后续代码,否则可以设置一些返回结果比如设置状态码,设置返回数据格式,统一返回响应内容
> 记得在前后打印日志 !!!

2.注册配置拦截器

代码如下(示例):


@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
    //注册web拦截器,实现拦截器功能和指定接口

    //注入“登录拦截器”
    @Autowired
    private LoginInterceptor loginInterceptor;

    //实现父类方法(注册拦截器)
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/book/**");
    }
}

在这里插入图片描述

代码说明

创建了WebConfig类 实现了 WebMvcConfigurer的接口 见名思意,“webmvc” 就要设计到一些前后端交互的内容,那么这个类真正的作用在于,注册拦截器,设置拦截器的使用目标,
代码体现在于,实现了父类的方法 addInterceptors (添加拦截器) ,根据参数registry来调用 addInterceptor方法进行添加我们定义的LoginInterceptor, addPathPatterns(“/**”)表示前后端交互的接口,对那些接口使用拦截器当然 也可以对这些接口中的特殊进行限制不使用拦截器 即excludePathPatterns方法


当用户访问一个接口时,第一时间并不是直接去和Controller进行交互,而是先通过“保安” (拦截器)验证身份之后才能顺利与Controller获取联系

拦截路径说明

在这里插入图片描述

拦截流程
正常的调用顺序:
在这里插入图片描述

有了拦截器之后,会在调用Controller之前进行相应的业务处理,执行的流程图如下
在这里插入图片描述

1.添加拦截器后,执行Controller方法之前,请求首先会被拦截器拦截住,执行preHandle()方法,这个方法需要返回一个布尔类型值,如果返回true,就表示放行本次操作,继续访问Controller中的方法,如果返货false,则不会放行
2.controller当中的方法执行完毕后,再回来执行PostHandle()这个方法以及afterCompletion()方法,执行完毕后,最终响应结果给浏览器

三,适配器

适配器模式定义:适配器模式,也叫包装器模式,将一个类的接口转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。
简单来说就是目标类不能直接使用,通过一个新类进行包装一下,适配器调用方使用。把两个不兼容的接口通过一定的方式使之兼容

比如下面两个接口,本身是不相容的(参数类型,个数都不一样)
在这里插入图片描述
通过适配器的方式,使之兼容
在这里插入图片描述
实际生活中的列子, 转换插头!!

对于java选手来说,面向对象变成 ,我们就得到实际摸得着的类入手
因此适配器模式角色可以分为以下几个对象

  • Target:目标接口,客服希望直接使用的接口
  • Adaptee:适配者,与Target不兼容
  • Adaptor:适配器类,此模式的核心,通过继承或者引用适配者的对象,把适配转为目标接口
  • client:需要使用适配器的对象

适配器模式的实现
场景:前面学习的skf4j就使用了适配器模式,slf4j提供了一系列打印日志的api,底层调用的是log4j或者logback来打印日志,我们作为调用者,止血药调用slf4j的api就行了
在这里插入图片描述
Slf4jApi为目标接口,Log4jApi用户接口 ,需要一个 Slf4jLog4jAdaptor适配器使两者兼容,Client是用户使用的类

目标接口
public interface Slf4jApi {
    //目标接口
    public void log(String log);
}



用户需要的接口
public class Log4jApi {
    public String print(String log) {
        return log;
    }
}



适配器
public class Slf4jLog4jAdaptor implements Slf4jApi {

    private Log4jApi log4jApi;
    public Slf4jLog4jAdaptor(Log4jApi log4jApi) {
        this.log4jApi = log4jApi;
    }
    @Override
    public void log(String log) {
        log4jApi.print(log);
    }
}
客服端调用
public class Client {
    public static void main(String[] args) {
        Slf4jApi slf4j = new Slf4jLog4jAdaptor(new Log4jApi());

        slf4j.log("打印日志");
    }
}


可以看出,我们不需要改变log4j的api(提前规定好的接口 ,不能修改),只能使用适配进行转换,就可以更换日志框架,保障系统的运行

应用场景

一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷,应用这种模式算是无奈之举,如果在设计初期,我们就能协调规避接口不兼容的问题,就不需要使用适配器模式了
所以适配器模式更多的应用场景主要是对正在运行的代码进行改造,并且希望可以复用原来代码的实现功能

四丶统一数据返回格式

在强制登录的案例中,我们做了两部分工作
1.通过Session来判断用户是否登录
2.对后端返回的数据进行封装,告诉前段处理的结果

对于第一个问题,拦截器可以实现对需要的接口进行了拦截的处理,以此判断用户是否登录,这一实现避免了之前对每个接口都要进行校验Session是否有值,而使用拦截器统一的处理会更方便,简单!!!
对于第二个问题,在之前我们需要对每一个接口进行封装,响应给前端的统一结果也有进行修改,因此,SpringBoot 提供了统一返回结果的功能

对接口进行封装
在这里插入图片描述

封装对象

@Data
public class Result<T> {
    private Integer code;
    private String errMsg;
    private T data;

    public static <T> Result<T> success(T data) {
        Result result = new Result();
        result.setCode(Constants.SUCCESS_CODE);
        result.setErrMsg("");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> unlogin() {
        Result result = new Result();
        result.setCode(Constants.UNLOGIN_CODE);
        result.setErrMsg("用户未登录");
        return result;
    }
    public static <T> Result<T> fail() {
        Result result = new Result();
        result.setCode(Constants.FAIL_CODE);
        result.setErrMsg("程序出错");
        return result;
    }
}

代码说明

统一返回Result对象
属性:
1.code 表示状态码 200 登陆成功 -1:未登录 -2:程序出错 通过 Constant静态常量 来自定义;
2.errMSg 表示错误信息 success(“”) ,fail(“程序出错”), unlogin(“未登录”);
3.data 表示各类接口返回的信息, 返回类型不同 , 使用泛型
方法: public static Result xxx(){}
1.success(T data) 表示登录成功, 创建并设置成功时的信息(code,errmsg,data),并返回这个对象
2.fail() 表示登录失败,程序出错,创建并设置失败的信息,返回该对象
3.unlogin 表示未登 , 创建并设置未登录的信息,返回该对象

现在,我们使用了统一结果返回的功能后,简化了对每个接口的封装效果,有效的整理成了如下代码

以下是代码演示:使目标接口返回结果统一是Result类型

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Result<?>){
            return body;
        }
        if (body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
        }
        return Result.success(body);
    }
}

代码说明

统一数据返回格式使用@ControllerAdvice 和 ResponseBodyAdvice的方式实现@ControAdvice表示控制器通知类,
**添加类ResponseAdvie,实现ResponseBodyAdvice接口,并在接口添加@ControllerAdvice注解

方法说明

1.supports方法,通过returnType参数来获取某个类某个方法
判断是否要执行beforeBodyWrite方法,true表示执行,false不执行,通过该方法可以选择那些类或那些方法的response要进行处理,其他的不进行处理,(上述代码直接设定了true表示对每个接口都要进行处理)

2.beforeBodyWrite方法,通过body来接收每个接口返回的响应response
如果一些方法结果已经是Result类型,直接返回body,不做处理
特例是若body是String类型,需要额外的进行处理(目前不懂这串代码):(具体含义是把Result对象转换为了String字符串,而不是对象,因此需要转换为JSON对象)
其他情况下,一律返回Result的类型

若需要测试,统一使用postman进行测试

优点:

1.方便前端程序员更好的接收和解析后端数据接口返回的数据
2.降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接口都是这样返回的
3.有利于项目统一数据的维护和修改
4.有利于后端技术部门的统一规范的标准规定,不会出现稀奇古怪的返回内容

五、统一异常处理

测试异常

@RestController
@RequestMapping("/test")
public class TestController {
	
    @RequestMapping("/t1")
    public String t1(){
        int a = 10/0;
        return "t1";
    }
	//数组越界异常
    @RequestMapping("/t2")
    public int t2(){
        int[] arr = new int[3];
        return arr[4];
    }
	//空指针异常
    @RequestMapping("/t3")
    public int t3(){
        String a = null;
        return a.length();
    }
}

捕获异常

@Slf4j
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice  {
    //捕获程序中出现的异常,进行处理
    @ExceptionHandler
    public Object hander(Exception e){
        log.error("发生异常 e:{}" , e);
        return Result.fail();
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler
    public Object handler(ArrayIndexOutOfBoundsException e){
        log.error("发生异常 e:{}" , e);
        return  Result.fail("发生数组越界异常...");
    }
    @ExceptionHandler
    public Object handler(NullPointerException e){
        log.error("发生异常 e:{}" , e);
        return Result.fail("发生空指针异常...");
    }
}

代码说明

建立ExceptionAdvice类, 使用了类@ControllerAdvice (表示控制器通知类)和方法注解@ExceptionHandler(表示异常处理器)进行捕获异常,两者结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件
如果程序中发生了异常,就会被改类捕捉到并且匹配到响应的异常,若匹配不到就直接可以匹配Exception的子类

注意
接口返回数据时,需要加上@ResponseBody注解 或则会返回视图导致找不到而报错!!!!

**@ResponseStatus()注解是用来设置状态码!!!**

总结

提示:这里对文章进行总结:

本节课我们实现了拦截器,统一返回数据格式,统一异常处理,实现了对每个接口进行了 “封装” ,在访问这个接口前执行了什么方法逻辑,或是在返回后的数据执行了什么方法逻辑, 又或者是在接口中出现问题的时候进行了统一的处理。这极大的抽取了共同的需求,和简化了真实的封装效果,为项目进一步的改善。

相关文章:

  • 《Vue Router实战教程》7.编程式导航
  • Java中工厂模式和抽象工厂模式的区别
  • 搜广推校招面经七十二
  • DeepSeek:穿透行业知识壁垒的搜索引擎攻防战
  • 基于神经环路的神经调控可增强遗忘型轻度认知障碍患者的延迟回忆能力
  • 第十七天 - Jenkins API集成 - 流水线自动化 - 练习:CI/CD流程优化
  • 展讯android15源码编译之apk单编
  • JavaWeb 课堂笔记 —— 08 请求响应
  • 蓝桥杯 Web 方向入门指南:从基础到实战
  • tauri2 程序如何拿到启动参数?例如-toen
  • Express中间件(Middleware)详解:从零开始掌握(3)
  • Go:程序结构
  • 金融科技数据安全
  • 责任链设计模式(单例+多例)
  • T5-LM 模型总结
  • go 通过汇编分析函数传参与返回值机制
  • NuGet包离线安装
  • Linux 入门七:从基础到进阶的文件操作
  • 【Ubutun】 在Linux Yocto的基础上去适配4G模块
  • 【AI提示词】创业导师提供个性化创业指导
  • 网站title 在哪里设置/网络整合营销4i原则
  • 本网站建设/汕头搜索引擎优化服务
  • 连云港网站建设案例/seo网站优化方案案例
  • 自己服务器建网站 备案/steam交易链接怎么获取
  • 黑群晖做网站/最新搜索引擎排名
  • 东营新闻联播在线直播今晚/aso优化的主要内容为