JavaEE高阶---SpringBoot 统一功能处理

news2024/9/29 15:31:39

一:什么是SpringBoot 统⼀功能处理

SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容:

  1. 统一用户登录权限验证;
  2. 统一数据格式返回;
  3. 统一异常处理。

二:统一用户登录权限验证

Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口的 preHandle(执⾏具体⽅法之前的预处理)方法。
  2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中。

此处我创建一个新的项目:

在这里插入图片描述

在这里插入图片描述

  1. 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体方法之前的预处理)⽅法。
package com.example.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 登录拦截器
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //登录判断业务
        HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute("userinfo") != null) {
            return true;
        }
        log.error("当前用户没有访问权限");
        response.setStatus(401);
        return false;
    }
}

  1. 将自定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors 方法中。
package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/login") // 排除不拦截的url
                .excludePathPatterns("/user/reg"); // 排除不拦截的url
    }
}

统一用户登录权限的验证步骤如下:

在这里插入图片描述

在这里插入图片描述

查看UserController中的具体方法[这里写了三个方法]:

package com.example.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.PasswordAuthentication;

@RestController
@RequestMapping("user")
@Slf4j
public class UserController {
    @RequestMapping("/login")
    public boolean login(HttpServletRequest request,
                         String username,String password) {
//        //1.非空判断
//        if(username != null && username != "" &&
//           password != null && password != "") {
//            //2.验证用户名和密码是否正确
//        }

        //1.非空判断[高端写法]
        if(StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
            //2.验证用户名和密码是否正确
            if(username.equals("admin") && password.equals("admin")) {
                //登录成功
                HttpSession session = request.getSession();
                session.setAttribute("userinfo","admin");
                return true;
            } else {
                //用户名或密码输入错误
                return false;
            }
        }
        return false;
    }

    @RequestMapping("getinfo")
    public String getInfo() {
        log.debug("执行了 getinfo 方法");
        return " 执行了 getinfo 方法";
    }

    @RequestMapping("reg")
    public String reg() {
        log.debug("执行了 reg 方法");
        return " 执行了 reg 方法";
    }
}

根据我们设定的拦截规则,只有执行getinfo()时才会被拦截。

执行reg方法,成功执行;

在这里插入图片描述

执行getinfo方法,拦截器生效:

在这里插入图片描述

在这里插入图片描述

这就是拦截器的基本实现。正常情况下的调用顺序是:

在这里插入图片描述

有拦截器之后,会在调用Controller之前进行相应的业务处理:

在这里插入图片描述

实现原理源码分析:

所有的 Controller 执⾏都会通过⼀个调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 控制台的打印信息看出,如下图所示:

在这里插入图片描述

⽽所有⽅法都会执⾏ DispatcherServlet 中的 doDispatch 调度⽅法,而在doDispatch调度方法中开始执⾏ Controller 之前,会先调用预处理⽅法 applyPreHandle,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执⾏拦截器中的 preHandle ⽅法,这样就和我们前⾯定义的拦截器对应上了。

拦截器小结:

Spring 中的拦截器也是通过动态代理和环绕通知的思想实现的,⼤体的调⽤流程如下:

在这里插入图片描述

三:统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个⽅法事件,具体实现代码如下:

package com.example.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;

/**
 * 统一异常处理
 */
@ControllerAdvice
public class ErrorAdvice {
    @ExceptionHandler(Exception.class)
    @ResponseBody //将Java对象转为json格式的数据
    public HashMap<String,Object> exceptionAdvice(Exception e) {
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",-1);
        result.put("msg","总异常信息为" + e.getMessage());
        return result;
    }

    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public HashMap<String, Object> arithmeticAdvie(ArithmeticException e) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", "-2");
        result.put("msg","算数运算异常" + e.getMessage());
        return result;
    }
}


在这里插入图片描述

此处设计一个算术运算异常用于检验:

    @RequestMapping("reg")
    public String reg() {
        int num = 10/0;
        log.debug("执行了 reg 方法");
        return " 执行了 reg 方法";
    }

在这里插入图片描述

此时我做这样一件事:

在这里插入图片描述

将算术异常处理的代码注释掉,再次访问reg方法:

在这里插入图片描述

此时显示为总的异常信息。这说明,如果当前代码中添加了更加具体的异常处理,就会走这个具体的异常处理逻辑;如果没有添加,就会走总的异常处理逻辑。

再设计一个空指针异常:


    @RequestMapping("reg")
    public String reg() {
        Object obj = null;
        System.out.println(obj.hashCode());
        log.debug("执行了 reg 方法");
        return " 执行了 reg 方法";
    }

在这里插入图片描述

四:统一数据返回格式

首先,为什么需要统一数据的返回格式呢?

统⼀数据返回格式的优点有很多,例如:

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

统⼀的数据返回格式可以使⽤ @ControllerAdvice【控制器通知器】 + ResponseBodyAdvice 的方式实现,具体实现代码如下:

package com.example.demo.config;

import org.springframework.beans.factory.parsing.ReaderEventListener;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

/**
 * 统一数据返回格式
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
//    返回 true 表示重写
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
//  方法返回之前调用此方法
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 构造统一返回对象
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("msg","");
        result.put("data",body);
        return result;
    }
}

在这里插入图片描述

访问具体的方法:

在这里插入图片描述
在这里插入图片描述

此时数据返回的格式就统一了!

但是当返回数据的格式为String时,此时出现异常了:

在这里插入图片描述

在这里插入图片描述

说明String类型的数据无法正确加载,这与转换器的加载时机有关。

正确做法是,对String类型的对象做单独的处理:

ResponseAdvice

package com.example.demo.common;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

/**
 * 统一数据返回封装
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    public static HashMap<String,Object> success(Object data) {
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("msg","");
        result.put("data",data);
        return result;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        if(body instanceof HashMap) {// 已经是封装好的对象
            return body;
        }
        if(body instanceof String) {// 返回对象是String类型(特殊)
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                return objectMapper.writeValueAsString(success(body));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return success(body);
    }
}


在这里插入图片描述

在这里插入图片描述

注意,在自定义拦截器时,我们添加了@Configuration注解;在进行统一异常处理和统一数据格式返回时,我们添加了@ControllerAdvice注解。只有添加了这些注解,Spring项目才能在执行到相应的逻辑时感知到统一格式处理逻辑的存在,进而触发下面的代码。说白了,要想实现SpringBoot统一功能的处理,就要遵循官方的书写规范。

本节内容到此结束!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scala 集合常用函数

文章目录集合常用函数一、基本属性和常用操作1、常用方法2、案例示例二、衍生集合1、衍生集合常用方法操作2、案例示例三、集合简单计算函数1、常用计算操作2、案例示例四、集合计算高级函数1、语义说明2、案例示例集合常用函数 一、基本属性和常用操作 1、常用方法 (1) 获取…

Release库与Debug库混用导致释放堆内存时产生异常的详细分析

目录 1、问题描述 2、使用Windbg启动Debug版本的exe程序进行分析 3、进一步分析 4、问题复盘 5、为什么Debug库与Release库混用可能会出异常&#xff1f; 6、最后 VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#xff09;ht…

DM8:dexpdimp-逻辑导出--逻辑导入

DM8:dexp&dimp-逻辑导出--逻辑导入1 dexp逻辑导出dmp文件1.1 全库导出命令附加的参数信息1.2 导出用户所拥有权限的数据库对象-命令附加的参数信息1.3 导出用户所拥有权限的数据库对象-命令附加的参数信息2 dimp--逻辑导入dmp文件2.1 全库导入dmp数据文件-命令附加的参数信…

人体姿态估计-论文精读--DeepPose: Human Pose Estimation via Deep Neural Networks

图 1. 除了关节的极端变异性外&#xff0c;许多关节几乎不可见。我们能猜测左图中右臂的位置&#xff0c;因为我们看到了姿势的其余部分&#xff0c;并预测了这个人的运动或活动。同样&#xff0c;右边的人的左半身也根本看不见。这些都是需要进行整体推理的例子。我们相信DNNs…

php处理支付宝应用网关给接口发送的post参数

php如何接收支付宝应用网关发送的POST请求方式,参数又是GET请求的数据格式配置支付宝应用网关如何接收支付宝异步通知(应用网关接收请求)将&连接的参数分割成数组实例&#xff1a;难点配置支付宝应用网关 首先要在服务器上写一个接口,然后将接口的访问地址设置在支付宝应用…

手把手教Arthas,不再怕排查线上问题了

Arthas是alibaba开源的java诊断工具&#xff0c;支持jdk6&#xff0c;采用命令行交互模式&#xff0c;可以防败的定位和诊断线上的程序运行问题。官方文档&#xff1a;https://arthas.aliyun.com/doc/一、Arthas使用场景是否有一个全局视角来查看系统的运行状况&#xff1f;为什…

六种方式,教你在SpringBoot初始化时搞点事情!

前言 在实际工作中总是需要在项目启动时做一些初始化的操作&#xff0c;比如初始化线程池、提前加载好加密证书....... 那么经典问题来了&#xff0c;这也是面试官经常会问到的一个问题&#xff1a;有哪些手段在Spring Boot 项目启动的时候做一些事情&#xff1f; 方法有很多…

卷积层里的多输入多输出通道、池化层

多输入多通道每个通道都有一个卷积核&#xff0c;结果是所有通道卷积结果的和。无论有多少输入通道&#xff0c;到目前为止我们只用到单输出通道。可以有多个三维卷积核&#xff0c;每个核生成一个输出通道。输出通道数是卷积层的超参数。每个输入通道有独立的二维卷积核&#…

为什么JavaScript这么难学啊?

前言 觉得Js难其实是很正常的&#xff0c;首先这证明你在某些知识点上没有理解透彻&#xff0c;JS挺多的知识点点其实是比较抽象的&#xff0c;比如闭包、原型和原型链等&#xff0c;其次便是不会变通运用&#xff0c;这主要是敲代码熟练度的问题&#xff0c;所以我针对你这种…

架构运维篇(六):MySQL 8.0启用BinLog 支持

上一篇&#xff1a;架构运维篇&#xff08;五&#xff09;&#xff1a;Centos7/Linux中安装RocketMQ 最新线上的项目终于到多个数据执行的问题&#xff0c;找了团队DBA发现云上的MySQL 默认是没有启用BinLog支持。 小编研究了一下很简单&#xff0c;不过中间也遇到一些坑可以给…

结构重参数化(Structural Re-Parameters)PipLine

文章目录BASICSstrcutural Inception算法思想算法核心算法架构Re-Parameter四部曲&#xff1a;ACNetACNet原理ACNet分析涨点原因推理阶段融合机制Re-Parameter四部曲&#xff1a;RepVGGRepVGG原理RepVGG分析RepVGG BlockStructural Re-Parameters融合conv2d和BN融合1x1conv转换…

【一文讲通】样本不均衡问题解决--下

1欠采样、过采样欠采样&#xff1a;减少多数类的数量&#xff08;如随机欠采样、NearMiss、ENN&#xff09;。过采样&#xff1a;尽量多地增加少数类的的样本数量&#xff08;如随机过采样、以及2.1.2数据增强方法&#xff09;&#xff0c;以达到类别间数目均衡。还可结合两者做…

地址解析协议ARP

目录地址解析协议ARP1、流程2、动态与静态的区别3、ARP协议适用范围地址解析协议ARP 如何从IP地址找出其对应的MAC地址&#xff1f; 1、流程 ARP高速缓存表 当主机B要给主机C发送数据包时&#xff0c;会首先在自己的ARP高速缓存表中查找主机C的IP地址所对应的MAC地址&#xf…

Linux常用命令——lsblk命令

在线Linux命令查询工具 lsblk 列出块设备信息 补充说明 lsblk命令用于列出所有可用块设备的信息&#xff0c;而且还能显示他们之间的依赖关系&#xff0c;但是它不会列出RAM盘的信息。块设备有硬盘&#xff0c;闪存盘&#xff0c;cd-ROM等等。lsblk命令包含在util-linux-ng…

ES报文辅助生成工具-JavaFX

此程序为基于 Java8 开发的 JavaFX Maven 工程&#xff0c;是 Java 组装ElasticSearch请求报文工具的辅助 Java 代码生成工具&#xff0c;方便开发者快速编写代码。现学现用&#xff0c;写得不好。 工具界面 代码 pom.xml <project xmlns"http://maven.apache.org/P…

Android:URLEncoder空格被转码为“+”号

Android前段和后端接口交互时&#xff0c;经常会遇到特殊字符&#xff0c;比如表情、特殊标点等&#xff0c;这样在Url中是无法识别的&#xff0c;需要进行转码&#xff0c;后端进行解码交互。 但当使用URLEncoder时&#xff0c;会发现字符串中的空格被转换成“”号&#xff0…

客服系统即时通讯IM开发(四)网站实现实时在线访客列表【唯一客服】网站在线客服系统...

在使用我的客服系统时&#xff0c;如果引入了我的js &#xff0c;就可以实时看到网站上的所有访客了 使用 WebSocket 技术来实现实时通信。 在访客登录或退出时&#xff0c;向指定客服的 WebSocket 客户端发送消息。例如&#xff0c;你可以在访客登录时&#xff0c;向指定客服…

测试用例的设计? 万能公式

万能公式(必背)&#xff1a;功能测试性能测试界面测试兼容性测试易用性测试安全测试功能测试 &#xff1a;可能来自于需求文档&#xff0c;也可能来自生活经验性能测试 &#xff1a;功能没有问题不代表性能是ok的&#xff0c;性能往往体现在一些极端情况界面测试 &#xff1a;颜…

Prometheus-基于Consul的自动注册

一、背景介绍 如果我们的物理机有很多&#xff0c;不管是基于"file_sd_config"还是"kubernetes_sd_config"&#xff0c;我们都需要手动写入目标target及创建目标service&#xff0c;这样才能被prometheus自动发现&#xff0c;为了避免重复性工作过多&#…

【182】Java8利用二叉查找树实现Map

本文利用二叉查找树写了一个Map&#xff0c;用来保存键值对。 二叉查找树的定义 二叉查找树又名二叉搜索树&#xff0c;英文名称是 Binary Search Tree&#xff0c;缩写BST。 二叉排序树&#xff0c;英文名称是 Binary Sorted Tree&#xff0c;缩写BST。 二叉查找树、二叉搜…