统一处理异常和记录日志

news2025/2/24 14:30:41

统一处理异常

SpringBoot设计,如果出现错误404或500,自动调用特定路径下的html页面(路径和名字都特定)。/templates/error/404.html、/templates/error/500.html。程序中有错误自动就调用该页面。
但是错误有异步请求错误,也想同时记录日志。则使用统一处理的方式,即全局配置。

@ControllerAdvice 是 Spring MVC 中的一个注解,用于定义全局控制器的通知(advice)。它允许您在整个应用程序范围内定义对控制器的异常处理、绑定属性以及其他全局控制器通知的方法。
具体来说,@ControllerAdvice 通常与 @ExceptionHandler、@InitBinder 和 @ModelAttribute 注解一起使用:

  • @ExceptionHandler: 用于定义在控制器中抛出指定类型异常时的处理方法。
  • @InitBinder: 用于定义在控制器中自定义数据绑定规则的方法。
  • @ModelAttribute: 用于定义在所有请求处理方法之前执行的方法,通常用于在模型中添加公共属性。

通过将 @ControllerAdvice 注解添加到类上,您可以在该类中定义这些通知方法,并在整个应用程序中共享它们,以便统一处理异常、数据绑定和模型属性。这样可以提高代码的重用性和可维护性,并使全局控制器的配置更加简洁和清晰。

	//手动重定向错误页面
    @RequestMapping(path = "/error", method = RequestMethod.GET)
    public String getErrorPage() {
        return "/error/500";
    }
// 是Controller全局配置类,不用对任何Controller再做配置,可以统一做Controller的全局配置。@ControllerAdvice用来修饰类。
// 异常处理方案@ExceptionHandler、绑定数据方案@ModelAttribute、绑定参数方案@DataBinder. 他们都用来修饰方法。
// 这里只演示,统一处理异常(@ExceptionHandler)
@ControllerAdvice(annotations = Controller.class) // 限定注解@Controller,否则组件扫描所有的bean
public class ExceptionAdvice {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);

    @ExceptionHandler({Exception.class})// 处理哪些异常?Exception是所有异常的父类,所有异常都处理
    // 有异常controller会传过来Exception
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 记录日志
        logger.error("服务器发生异常:" + e.getMessage());//异常的概括
        for (StackTraceElement element : e.getStackTrace()) {//把异常所有栈的信息都记录下来
            logger.error(element.toString());
        }

        // 给浏览器响应
        // 要看是什么请求,想要服务器返回网页html/异步请求JSON(xml).从请求的消息头获取。
        String xRequestedWith = request.getHeader("x-requested-with");
        if ("XMLHttpRequest".equals(xRequestedWith)) {// 异步请求
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();// 输出流
            writer.write(CommunityUtil.getJSONString(1,"服务器异常!"));// 输出JSON字符串
        }else{// 请求html,重定向到错误页面
            response.sendRedirect(request.getContextPath() + "/error");
        }

    }
}

统一记录日志

记录日志,不一定有异常。拦截器也是针对控制器的。没有对业务组件、数据访问层统一处理。

想对业务层统一记录日志,而统一记录日志是系统功能,不要和业务功能混在一起实现。否则在想对记录日志的位置进行改变时,将会非常麻烦,因为业务bean有很多个,需要修改的时候得一个个改。

由此引入了AOP的方式,即面向切面编程,切面是一个一个组件。业务Bean是一个一个target。我们要先声明切点的位置,再通知要做什么事。只需要对切面组件编程即可,不需要再进到业务Bean中去改,提升了编程效率。
Aspect切面:

  • 注解@Component @Aspect
  • 声明切点的位置@Pointcut(切点的位置:返回值 包.类.方法.参数) pointcut()
  • 通知具体逻辑,5个注解@Before @After AfterReturning @AfterThrowing @Around
    Target: 是业务Bean
    在这里插入图片描述
    AOP实现有两种:
    AspectJSpring AOP。一般用后者即可。它是运行时织入,通过代理的方式,只在方法处有连接点。Spring AOP(面向切面编程)通常通过代理的方式来实现,主要有以下几个原因:
  • 无侵入性: 通过代理方式实现 AOP 可以避免对现有代码的侵入性。即使目标类没有实现任何接口,也可以通过 Spring AOP实现切面功能。
  • 动态性: 代理方式允许在运行时动态地应用切面。这意味着可以在运行时决定是否应用切面,以及如何应用切面,而无需在编译时硬编码切面逻辑。
  • 单一职责原则: 通过代理方式实现 AOP可以使目标类专注于自身的业务逻辑,而将横切关注点(如日志记录、事务管理等)从目标类中解耦出来,符合单一职责原则。
  • 多个切面组合:代理方式允许将多个切面组合应用于目标类,而无需修改目标类的代码。这种灵活性使得可以根据需求组合不同的切面,实现更加复杂的功能。
  • 易于管理: 通过代理方式实现的切面可以集中管理,例如在配置文件中声明切面和通知的关系,而无需在每个目标类中显式地声明切面逻辑。

统一记录日志示例

@Component
@Aspect
public class ServiceLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {// 参数:连接点
        // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteHost();
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        String target = joinPoint.getSignature().getDeclaringTypeName() + "." +joinPoint.getSignature().getName();// 得到该连接点的类名和方法名
        logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
    }
    
    @After("pointcut()")
    public void after() {
        System.out.println("after");
    }

    @AfterReturning("pointcut()")
    public void afterRetuning() {
        System.out.println("afterRetuning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 参数:连接点
        System.out.println("around before");
        Object obj = joinPoint.proceed();// 连接点调用目标组件的方法,返回目标组件的返回值
        System.out.println("around after");
        return obj;
    }
}

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

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

相关文章

Threejs实现闪电效果

这是一次比较失败的功能实现,本来想网上很少有threejs实现闪电效果的,但是我觉得好像可以做出来,就尝试着做了,结果做出来的太丑了,但是不能时间白费,所以记录下,总得有个交代。 首先还是搭建出…

14-pyspark的DataFrame使用总结

目录 前言DataFrame使用总结 DataFrame的构建方法1:通过列表构建方法2:通过Row对象构建方法3:通过表Schema构建 方法4:rdd结合字符串构建 DataFrame的方法 PySpark实战笔记系列第五篇 10-用PySpark建立第一个Spark RDD(PySpark实战…

DSP笔记-7时钟和系统控制

外部时钟源-晶振 无源晶振,有源晶振, 频率:20MHz,12.5MHz,30MHz,50MHz,32.768MHz SPI,SCI, I2C串行通信 eCAP捕获功能,eQEP解码,ePWM脉冲宽度…

SAP SD学习笔记03 - SD模块中的主数据

上一章讲了SD中的组织单位和SD的简单流程。 SAP SD学习笔记02 - 销售流程中的组织单位-CSDN博客 SAP SD学习笔记01 - 简单走一遍SD的流程:受注,出荷,请求-CSDN博客 这一章讲SD中的主数据: - 得意先Master(客户&…

Java设计模式之创建型模式(二)原型模式

原型模式 1、原型模式1-1、应用场景1-2、举个 软栗子1-3、举个 硬栗子1-4、举个实务栗子1-5、代码重构 学习原型模式的目的:原型模式的目的在于通过复制现有的实例来创建新的对象,以避免通过构造函数创建对象时可能带来的性能开销,同时可以控…

实验9 内置对象application

一、实验目的 掌握怎样在JSP中使用内置对象application 二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握application的用法。【参考课本例题4-16 留言板 】 三、源代码以及执行结果截图&#xff1a; example4_16.jsp <% page language"…

python——条件语句

概念 条件语句&#xff0c;简单的理解就是 满足条件执行这些代码&#xff0c;不满足则执行另一些代码 语法 #mermaid-svg-ITs2kv8f87vZuQhT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ITs2kv8f87vZuQhT .erro…

【网站项目】鲜花销售微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

excle如何设置自动显示星期几

目录 一.目的 二.范例结果 三.处理方式 一.目的 excle依据日期&#xff0c;设置后自动显示星期几。 二.范例结果 三.处理方式 公式1TEXT(XXX,DDDD) 公式2TEXT(XXX,DDD)

YoloV8改进策略:Block改进|轻量级的Mamba打造优秀的YoloV8|即插即用,简单易懂|附Block结构图(独家原创)更新中。。。。。

摘要 无Mamba不狂欢&#xff0c;今天给大家带来一个基于轻量级Mamba的改进。模块简单易懂&#xff0c;即插即用&#xff01; 带领大家去征服更高的领域。 论文&#xff1a;《LightM-UNet&#xff1a;Mamba 辅助的轻量级 UNet 用于医学图像分割》 https://arxiv.org/pdf/2403…

欢迎加入PenPad Season 2 ,获得勋章以及海量 Scroll 生态权益

PenPad 是 Scroll 生态中的首个 LaunchPad 平台&#xff0c;该平台继承了 Scroll 生态的技术优势&#xff0c;具备包括隐私在内的系列特点&#xff0c;同时且也被认为是 Scroll 生态最重要的价值入口之一。Penpad 与 Scroll 官方始终保持着合作&#xff0c;同时该项目自启动以来…

解决VM报错:不支持虚拟化的 amd-v/rvi

安装了VMware之后&#xff0c;想测试一下虚拟机嵌套。在勾选虚拟机CPU的虚拟化AMD-V/RVI之后&#xff0c;竟然无法启动&#xff0c;提示“此平台不支持虚拟化的 amd-v/rvi”。 上网找了一下资料&#xff0c;发现是因为Hyper-V与VMware冲突以及Windows Defender的内核隔离导致的…

【基础知识】计算机国企爱考的二进制知识、大学生必须掌握

二进制与十进制相互转换的详细过程及例题解析 二进制和十进制是两种常用的数制系统。二进制系统仅使用0和1两个数字&#xff0c;而十进制系统则使用0到9的十个数字。在计算机科学和数字电路设计等领域&#xff0c;经常需要在这两种数制之间进行转换。本文将详细介绍二进制与十…

(二)ffmpeg 下载安装以及拉流推流示例

一、ffmpeg下载安装 官网&#xff1a;https://www.ffmpeg.org/ 源码下载地址&#xff1a;https://www.ffmpeg.org/download.html#releases 下载源码压缩包 下载完成之后解压并在该目录下打开命令窗口 安装依赖环境&#xff1a; sudo apt-get install build-essential nasm …

组合数学<1>——组合数学基础

今天我们聊聊组合数学。(本期是给刚刚学习组合数学的同学看的&#xff0c;dalao们可以自行忽略) 建议:不会求逆元的出门左转数论<2>&#xff0c;不会数论的出门右转数论<1>。 加乘原理 加乘原理小学奥数就有。 总的来说:加法原理:分类;乘法原理:分步 比如说&a…

OJ 【难度1】【Python】完美字符串 扫雷 A-B数对 赛前准备 【C】精密计时

完美字符串 题目描述 你可能见过下面这一句英文&#xff1a; "The quick brown fox jumps over the lazy dog." 短短的一句话就包含了所有 2626 个英文字母&#xff01;因此这句话广泛地用于字体效果的展示。更短的还有&#xff1a; "The five boxing wizards…

【我的小工具】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。

存储革新:下一代低功耗PCM相变存储器

引言 由于Optane&#xff08;实质为PCM相变存储器&#xff09;被intel放弃以后&#xff0c;小编一直在关注业内有关SCM存储级内存&#xff08;PCM、ReRAM等&#xff09;相关的研究进展。比如之前发布的内容&#xff0c;供存储随笔的读者参考&#xff01; 字节跳动入局存储内存…

如何应用电桥电路的原理?

电桥电路是一种常用的测量技术&#xff0c;它利用了四个电阻的网络来检测电路的平衡状态。在平衡状态下&#xff0c;电桥的输出电压为零&#xff0c;这种特性使得电桥电路非常适合于精确测量电阻、电感、电容等电气参数&#xff0c;以及用于传感器和测量设备中。以下是电桥电路…

第十二届蓝桥杯真题做题笔记

2、卡片 笔记&#xff1a; 直接巧用排列组合求解即可&#xff1a; 我们通过对样例说明进行分析可知&#xff1a;想要分给n个小孩&#xff0c;那么我们就需要满足C(K, 2) K > n才能满足。 #include<bits/stdc.h> using namespace std;int com(int up, int down){i…