8.统一异常处理 + 统一记录日志

目录

1.统一异常处理

2.统一记录日志


1.统一异常处理


在 HomeController 类中添加请求方法(服务器发生异常之后需要统一处理异常,记录日志,然后转到 500 页面,需要人工处理重定向到 500 页面,提前把 500 页面请求访问配置)

    @RequestMapping(path = "/error", method = RequestMethod.GET)public String getErrorPage() {return "/error/500";}

在 controller 类下新建 advice 包,创建 ExceptionAdvice 类:

  • 添加注解 @ControllerAdvice 统一处理异常:此时这个组件会扫面所有的 Bean,做一个限制 @ControllerAdvice(annotations = Controller.class):这个组件只去扫面带有 Controller 注解的 Bean
  • 添加所有方法处理所有错误情况:添加注解 @ExceptionHandler 表示这个方法处理所有异常的方法—— @ExceptionHandler({Exception.class})
  • 通常参数为 Exception e, HttpServletRequest request, HttpServletResponse response
  • 注入日志,并且把异常记录日志,想要把异常非常详细的栈的信息记录:遍历栈的信息得到的是数组(每次遍历得到一条异常信息,打印日志)
  • 然后给浏览器响应(重定向到错误页面)
  • 这时候还需要判断这个请求是普通请求还是异步请求:浏览器访问服务器可能是普通请求(希望返回网页,然后重定向到 500)也可能是异步请求(希望返回 JSON,不可以返回到页面 HTML)
  • 通过 request.getHeader("x-requested-with") 获取请求返回 String:如果返回值等于 "XMLHttpRequest"则表示为异步请求,这个请求是以 XML 的形式访问,希望返回 XML,只有异步请求才希望返回 XML(普通请求返回 HTML)然后响应字符串("application/plain;charset=utf-8":向浏览器返回普通字符串,可以是 JSON 格式,需要人为的将字符串转化为 JS 对象),获取输出流输出JSON字符串;如果是普通请求,重定向到错误页面(获取项目访问路径 + "/error" 路径)
package com.example.demo.controller.advice;import com.example.demo.util.CommunityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;//添加注解 @ControllerAdvice 统一处理异常:此时这个组件会扫面所有的 Bean,
//做一个限制 @ControllerAdvice(annotations = Controller.class):这个组件只去扫面带有 Controller 注解的 Bean
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);//添加所有方法处理所有错误情况:添加注解 @ExceptionHandler 表示这个方法处理所有异常的方法@ExceptionHandler({Exception.class})public void handleException(Exception e, HttpServletRequest request,HttpServletResponse response) throws IOException {//注入日志,并且把异常记录日志,想要把异常非常详细的栈的信息记录// 遍历栈的信息得到的是数组(每次遍历得到一条异常信息,打印日志)logger.error("服务器发生异常: " + e.getMessage());for (StackTraceElement element : e.getStackTrace()) {logger.error(element.toString());}//然后给浏览器响应(重定向到错误页面)//这时候还需要判断这个请求是普通请求还是异步请求:浏览器访问服务器可能是普通请求(希望返回网页,然后重定向到 500)// 也可能是异步请求(希望返回 JSON,不可以返回到页面 HTML)//通过 request.getHeader("x-requested-with") 获取请求返回 String:如果返回值等于 "XMLHttpRequest"则表示为异步请求,// 这个请求是以 XML 的形式访问,希望返回 XML,只有异步请求才希望返回 XML(普通请求返回 HTML)String xRequestedWith = request.getHeader("x-requested-with");if ("XMLHttpRequest".equals(xRequestedWith)) {// 然后响应字符串("application/plain;charset=utf-8":向浏览器返回普通字符串//可以是 JSON 格式,需要人为的将字符串转化为 JS 对象)response.setContentType("application/plain;charset=utf-8");//获取输出流输出JSON字符串;如果是普通请求,重定向到错误页面(获取项目访问路径 + "/error" 路径)PrintWriter writer = response.getWriter();writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));} else {response.sendRedirect(request.getContextPath() + "/error");}}
}

2.统一记录日志

  • 可不可以利用控制器通知统一处理?控制器通知在发生异常时可以统一处理,而统一记录日志并不是发生异常才记录
  • 可不可以使用拦截器?拦截器也是针对控制器做处理,而记录日志并不是只有在控制器中记录(目前我们对业务组件做日志记录)
  • 使用传统方法:把记录日志的内容封装到组件中,在不同的业务组件(service)方法中调用(比如在一开始记录日志,在写也业务方法之前记录日志)。这种方法也有弊端:业务组件方法是处理业务,但是在之前添加日志需求(不是业务需求,是系统需求),所以在业务方法中耦合了系统需求是有弊端的,例如现在系统需求发生变化,不想在业务之前记录日志,而是在之后记录日志,改动比较大,很麻烦
  • 解决问题的最好方法:将记录日志(系统需求)单独实现——使用 AOP,单独定义一个组件,不和业务组件发生直接关系,将业务组件通用逻辑封装在这个组件中

对于 AOP 的知识可以阅读之前学习的 AOP 博客:Spring AOP-CSDN博客

在 pom.xml 添加依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

对所有业务组件记录日志(在业务组件一开始记录日志),新建 aspect 包创建 ServiceLogAspect 类(存放切面主键):

  • 添加注解 @Component、@Aspect
  • 实例化 Logger
  • 声明切点:所有业务组件都去处理
  • 使用前置通知在业务组件一开始记录日志
  • 记录格式:用户[1.2.3.4],在[xxx],访问了[com.example.demo.service.xxx()].
  • 用户 ip 通过 request 获取,获取 request:RequestContextHolder.getRequestAttributes();
  • 拼接时间:new Date,然后实例化
  • 访问某个类某个方法(类名 + 方法名):给方法添加 JoinPoint 连接点参数,连接点指代程序植入的目标方法
  • 最后再进行全部拼接
package com.example.demo.aspect;import org.aspectj.lang.JoinPoint;
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;
import java.text.SimpleDateFormat;
import java.util.Date;@Component
@Aspect
public class ServiceLogAspect {//实例化 Loggerprivate static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);//声明切点:所有业务组件都去处理@Pointcut("execution(* com.example.demo.service.*.*(..))")public void pointcut() {}//使用前置通知在业务组件一开始记录日志@Before("pointcut()")public void before(JoinPoint joinPoint) {//记录格式:用户[1.2.3.4],在[xxx],访问了[com.example.demo.service.xxx()].//用户 ip 通过 request 获取,获取 request:RequestContextHolder.getRequestAttributes();ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String ip = request.getRemoteHost();//拼接时间:new Date,然后实例化String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//访问某个类某个方法(类名 + 方法名):给方法添加 JoinPoint 连接点参数,连接点指代程序植入的目标方法String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();//全部拼接logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));}
}

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

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

相关文章

经典神经网络——AlexNet模型论文详解及代码复现

一、背景 AlexNet是在2012年由Alex Krizhevsky等人提出的&#xff0c;该网络在2012年的ImageNet大赛上夺得了冠军&#xff0c;并且错误率比第二名高了很多。Alexnet共有8层结构&#xff0c;前5层为卷积层&#xff0c;后三层为全连接层。 论文地址&#xff1a;ImageNet Classif…

ModuleNotFoundError: No module named ‘mdtex2html‘ module已经安装还是报错,怎么办?

用streamlit运行ChatGLM/basic_model/web_demo.py的时候&#xff0c;出现了module not found&#xff1a; ModuleNotFoundError: No module named mdtex2html Traceback: File "/home/haiyue/.local/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script…

【阿里云】图像识别 智能分类识别 增加网络控制功能点(三)

一、增加网络控制功能 实现需求TCP 心跳机制解决Soket异常断开问题 二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。 查看当前系统的TCP KeepAlive参数修改TCP KeepAlive参数 三、C语言实现TCP KeepAlive功能 四、setsockopt用于设置套接字选项的系…

Qt4利用MVC开发曲线数据编辑器

目录 1 需求 2 开发流程 1 搭建框架 2 构造函数 3 打开工程 4 实现应用程序参数加载 5 QCustomPlot和TableView的联动 6 数据的可视化修改 7 列表点击事件事先键盘控制 8 表格实现复制&#xff0c;粘贴&#xff0c;删除等一系列功能 9 曲线实现自适应范围和统一范围…

【JMeter】运行方式

第一种&#xff1a; 使用GUI 操作&#xff1a; 在JMeter界面菜单导航上点击运行按钮 一般用作创建TestPlan和调试脚本增加java堆空间来满足测试环境 第二种&#xff1a;使用CLI(Command Line) 性能测试一般请求量比较大&#xff0c;为了节省资源 CLI参数用法&#xff1a; 字段…

Flask Echarts 实现历史图形查询

Flask前后端数据动态交互涉及用户界面与服务器之间的灵活数据传递。用户界面使用ECharts图形库实时渲染数据。它提供了丰富多彩、交互性强的图表和地图&#xff0c;能够在网页上直观、生动地展示数据。ECharts支持各种常见的图表类型&#xff0c;包括折线图、柱状图、饼图、散点…

[Spring] 字节一面~Spring 如何解决循环依赖问题 以及 @resource 与 @autowire 同时存在时谁生效

文章目录 Spring 如何解决循环依赖问题resource 与 autowire 同时存在时谁生效 Spring 如何解决循环依赖问题 Spring在实例化一个bean的时候&#xff0c;是首先递归实例化其所依赖的所有bean&#xff0c;直到某个bean没有依赖其他bean&#xff0c;此时就会将该实例返回&#x…

【JavaWeb】Servlet

Servlet 文章目录 Servlet一、简介二、开发流程三、生命周期四、ServletConfig和ServletContext五、HttpServletRequest常见API六、HttpServletResponse常见API七、请求转发和响应重定向7.1 概述7.2 请求转发7.3 响应重定向 八、请求与响应乱码问题8.1 GET与POST请求乱码8.2 响…

内网穿透的应用-Jupyter Notbook+cpolar内网穿透实现公共互联网访问使用数据分析工作

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

五、Lua流程控制与函数

一、流程控制 &#xff08;一&#xff09;含义 Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码&#xff0c;在条件为 false 时执行其他指定代码。 &#xff08;二&#xff09;原型 if (成立) then执行体1else执行体2 end…

字符串入门算法题!

概述 字符串和数组一样算是比较简单的题目&#xff0c;正适合打算法基础&#xff0c;一定要认真对待&#xff01;&#xff01;&#xff01; 字符串类型的算法问题可以分为简单、中等和困难的难度级别&#xff0c;基础类型一些基本的字符串处理问题&#xff0c;如字符串的拼接…

自动化部署 扩容openGauss —— Ansible for openGauss

前言 大家好&#xff0c;今天我们为大家推荐一套基于Ansible开发的&#xff0c;自动化部署及扩容openGauss的脚本工具&#xff1a;Ansible for openGauss&#xff08;以下简称 AFO&#xff09;。 通过AFO&#xff0c;我们只需简单修改一些配置文件&#xff0c;即可快速部署多种…

数智赋能 锦江汽车携手苏州金龙打造高质量盛会服务

作为一家老牌客运公司&#xff0c;成立于1956年的上海锦江汽车服务有限公司&#xff08;以下简称锦江汽车&#xff09;&#xff0c;拥有1200多辆大巴和5000多辆轿车&#xff0c;是上海乃至长三角地区规模最大的专业旅游客运公司。面对客运市场的持续萎缩&#xff0c;锦江汽车坚…

王道数据结构课后代码题p19 第14题请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c) 中的最小距离。(c语言代码实现)

本题其实就是找a到c的最小值 有讲解p19 第14题 c语言实现王道数据结构课后代码题_哔哩哔哩_bilibili 下方有图&#xff1a; 本题代码如下 int abs(int a)//计算绝对值 {if (a < 0)return -a;elsereturn a; } int min(int a, int b, int c)//a是否为三个数中的最小值 {if …

基于xml配置的AOP

目录 xml方式AOP快速入门 xml方式AOP配置详解 xml方式AOP快速入门 xml方式配置AOP的步骤 导入AOP相关坐标 <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></de…

二叉树题目:结点与其祖先之间的最大差值

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;结点与其祖先之间的最大差值 出处&#xff1a;1026. 结点与其祖先之间的最大差值 难度 5 级 题目描述 要求 给…

无人售货奶柜:方便快捷,新鲜畅享

无人售货奶柜&#xff1a;方便快捷&#xff0c;新鲜畅享 无人售货奶柜&#xff0c;便捷、多样、实时监控。随时选择您喜爱的奶制品和饮料&#xff0c;快速支付&#xff0c;满足个性口味。24小时全天候运营&#xff0c;无时间限制&#xff0c;随时满足您的购物需求。借助先进的技…

在Rust中编写自动化测试

1.摘要 Rust中的测试函数是用来验证非测试代码是否是按照期望的方式运行的, 测试函数体通常需要执行三种操作:1.设置任何所需的数据或状态;2.运行需要测试的代码;3.断言其结果是我们所期望的。本篇文章主要探讨了Rust自动化测试的几种常见场景。 2.测试函数详解 在Rust项目工…

使用Python编写数独游戏Sudoku教程

数独是各种应用程序中流行的益智类拼图游戏。数独板是一个99的网格&#xff0c;玩家必须在每行、每列和33的子网格中放置一次数字1到9&#xff0c;并且只能放置一次。游戏开始时&#xff0c;有几个空格已经用数字填充&#xff0c;称为givens。一个好的数独谜题应该只有一个可能…

Linux MeterSphere一站式开源持续测试平台远程访问

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…