Day25:统一处理异常、AOP编程、统一记录日志

表现层在最外面,异常在这层处理。

SpringBoot处理异常的简单实现

把error文件夹放在templates文件夹下,html命名为状态吗:

image

修改404.html和500.html为模版(注意图片路径修改为动态)

image

更细粒度的处理异常的方式

  • @ControllerAdvice

    • 用于修饰类,表示该类是Controller的全局配置类。
    • 在此类中,可以对Controller进行如下三种全局配置:

    异常处理方案、绑定数据方案、绑定参数方案。

  • @ExceptionHandler

    • 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。
  • @ModelAttribute

    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。
  • @DataBinder

    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。

示例

  1. 在HomeController中添加如下方法:
@RequestMapping(path = "/error", method = RequestMethod.GET)
public String getErrorPage() {return "/error/500";
}
  1. 在controller包下新加一个包叫Advice,new一个class ExceptionAdvice
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {private static final Logger logger = getLogger(ExceptionAdvice.class);@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());}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, "服务器异常"));} else {response.sendRedirect(request.getContextPath() + "/error");}}
}
  • annotations = Controller.class是@ControllerAdvice注解的一个属性,它用于指定这个advice应该应用于哪些类。在您的代码中,annotations = Controller.class表示这个advice应该应用于所有带有@Controller注解的类。
  • 。getLogger(ExceptionAdvice.class)是一个静态方法,它返回一个与指定类(在这里是ExceptionAdvice类)关联的Logger实例。(.class不要漏)
  • 异步请求的request.getHeader(“x-requested-with”) 是XMLHttpRequest
  • getWriter()是HttpServletResponse接口中的一个方法,它返回一个PrintWriter对象,可以用来向客户端发送字符文本。这个PrintWriter对象会自动使用响应的字符编码(通过setCharacterEncoding方法设置),所以你可以直接写入字符串,而不需要转换为字节。

AOP编程

  • Aspect Oriented Programing, 即面向方面(切面)编程。
  • AOP是一种编程思想,是对OOP的补充, 可以进一步提高编程的效率。

image

(相当于一刀切开,在每一层都用到)

image

  • Target:在AOP编程中,目标(Target)是指被一个或多个切面(Aspect)所通知(advise)的对象。也就是说,目标是包含业务逻辑的类,这些类的某些方法将被切面中的通知所增强。
  • Joinpoint:连接点(Joinpoint)是指在程序执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。在Spring AOP中,一个连接点总是表示一个方法的执行。
  • Aspect:切面(Aspect)是一个模块,它包含一些通知和切入点。通知(Advice)是切面在特定的连接点(Joinpoint)上执行的代码,切入点(Pointcut)则定义了这些通知应该在何处执行。切面的作用是将通用的功能从业务逻辑中分离出来,以实现代码的重用和解耦。
  • Weaving:织入(Weaving)是将切面插入到目标对象以创建一个被通知的对象的过程。这个过程可以在编译时(如AspectJ的编译器),加载时或运行时完成。Spring AOP默认在运行时进行织入。
  • Pointcut:切入点(Pointcut)是指在哪些Joinpoint(连接点)上应用Advice(通知)。它是一个表达式,用于匹配方法执行的点。例如,你可以定义一个切入点来匹配所有在某个特定类中的方法,或者所有的setter方法,等等。在Spring AOP中,切入点表达式通常使用AspectJ的切入点表达式语言。
  • Advice:通知(Advice)是切面(Aspect)在特定的连接点(Joinpoint)上执行的代码。它是切面的主要内容,定义了切面要完成的工作。在Spring AOP中,通知可以是以下五种类型之一:前置通知(Before),后置通知(After),返回通知(After-returning),异常通知(After-throwing)和环绕通知(Around)。前置通知在连接点之前执行,后置通知在连接点之后执行,返回通知在连接点正常返回后执行,异常通知在连接点抛出异常后执行,环绕通知可以在连接点前后都执行。

AOP的实现

AspectJ(学习代价高)

  • AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。
  • AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。

Spring AOP(常用性价比最高)

  • Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。
  • Spring AOP在运行时通过代理的方式织入代码(记住!!!),只支持方法类型的连接点。
  • Spring支持对AspectJ的集成。

Spring AOP

JDK动态代理(Spring默认,必须有接口)

  • Java提供的动态代理技术,可以在运行时创建接口的代理实例。
  • Spring AOP默认采用此种方式,在接口的代理实例中织入代码。

CGLib动态代理

  • 采用底层的字节码技术,在运行时创建子类代理实例。
  • 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。

AOP示例

在Controller下创建一个新的包Aspect,创建类AlphaAspect:

@Component
@Aspect
public class AlphaAspect {//定义切点//service包下的所有方法、所有参数、所有返回值@Pointcut("execution(* com.newcoder.community.service.*.*(..))")public void pointcut() {}//定义通知//在切点之前执行@Before("pointcut()")public void before() {System.out.println("before");}@After("pointcut()")public void after() {System.out.println("after");}@AfterReturning("pointcut()")public void afterReturning() {System.out.println("afterReturning");}@AfterThrowing("pointcut()")public void afterThrowing() {System.out.println("afterThrowing");}@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before obj");Object obj = joinPoint.proceed();System.out.println("after obj");return obj;}
}
  • @Pointcut(“execution(* com.newcoder.community.service..(…))”)注解表示制定生效的路径,这里是service包下的所有方法、所有参数、所有返回值;
  • @Before(“pointcut()”)在函数之前、@After(“pointcut()”)在函数之后、@AfterReturning(“pointcut()”)函数返回之后、@AfterThrowing(“pointcut()”)抛出异常之后;
  • 注意@Around(“pointcut()”)实在执行之间,通过Object obj = joinPoint.proceed();这个就相当于执行业务组件了:

这段代码是一个环绕通知(Around Advice),它是AOP(面向切面编程)中的一种通知类型。环绕通知可以在方法调用前后都执行一些代码,并且可以决定是否执行目标方法。

@Around(“pointcut()”)注解表示这个通知应用于pointcut()定义的切入点。在这个例子中,pointcut()定义的切入点是com.newcoder.community.service包下的所有方法。 around方法的参数ProceedingJoinPoint joinPoint是一个特殊的JoinPoint,它代表了切入点,也就是要被通知的目标方法。

在around方法中,首先执行System.out.println(“before obj”);打印一条消息,然后调用joinPoint.proceed()执行目标方法,并将结果保存在obj变量中。**joinPoint.proceed()方法的调用是必须的,否则目标方法不会被执行。**然后执行System.out.println(“after obj”);打印一条消息,最后返回目标方法的结果。 所以,这个环绕通知在目标方法执行前后都打印了一条消息,并且返回了目标方法的结果。

运行后可以发现输出了很多:

image

统一记录日志

需求:对所有的组件及日志(这是系统需求与业务需求耦合这样不好)

解决:面向AOP切面编程。

创建ServiceLogApect.java:

@Component
@Aspect
public class ServiceLogAspect {private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);//定义切点//service包下的所有方法、所有参数、所有返回值@Pointcut("execution(* com.newcoder.community.service.*.*(..))")public void pointcut() {}@Before("pointcut()")public void before(JoinPoint joinPoint) {//用户[1.2.3.4],在[xxx]时间,访问了[com.newcoder.community.service.xxx()]。logger.debug("before");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));}
}
  • ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes():得到当前hold的request的属性;
  • String ip = request.getRemoteHost();得到IP
  • String target = joinPoint.getSignature().getDeclaringTypeName() + “.” + joinPoint.getSignature().getName();得到切面切到的JointCut的签名

最后可以看到日志有这些输出:

image

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

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

相关文章

单例模式如何保证实例的唯一性

前言 什么是单例模式 指一个类只有一个实例,且该类能自行创建这个实例的一种创建型设计模式。使用目的:确保在整个系统中只能出现类的一个实例,即一个类只有一个对象。对于频繁使用的对象,“忽略”创建时的开销。特点&#xff1a…

目标检测+车道线识别+追踪

一种方法: 车道线检测-canny边缘检测-霍夫变换 一、什么是霍夫变换 霍夫变换(Hough Transform)是一种在图像处理和计算机视觉中广泛使用的特征检测技术,主要用于识别图像中的几何形状,尤其是直线、圆和椭圆等常见形状…

(编程实用技巧)如何减少内存占用和提高运算速度?(C语言)

一、减少内存 减少C语言程序内存占用可以从以下几个方面入手: 1. **合理选择数据类型**: - 根据实际需求选择适当的数据类型,避免过大类型造成不必要的内存消耗。例如,如果只需要表示较小的整数,可以使用uint8_t而非i…

踏上机器学习之路:探索数据科学的奥秘与魅力

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

samba实现linux共享文件夹

一、samba安装 sudo apt install samba 二、配置Samba 编辑Samba配置文件sudo vi /etc/samba/smb.conf 在文件末尾添加以下内容,设置一个简单的共享目录(替换path_to_share为实际的共享目录路径): [Share] path /path_to_sha…

Docker进阶:使用Docker部署Harbor私有镜像仓库

Docker进阶:使用Docker部署Harbor私有镜像仓库 1、安装Docker和Docker Compose1、安装Docker、Docker Compose2、验证Docker和Docker Compose是否成功安装3、先启动运行docker服务 2、下载并配置Harbor1、下载最新版本的Harbor离线安装包2、配置Harbor的主机名和管理…

C#全新一代医院手术麻醉系统围术期全流程源码

目录 一、麻醉学科的起源 二、麻醉前访视与评估记录单 患者基本信息 临床诊断 患者重要器官功能及疾病情况 病人体格情况分级 手术麻醉风险评估 拟施麻醉方法及辅助措施 其他需要说明的情况 访视麻醉医师签名 访视时间 与麻醉相关的检查结果 三、手术麻醉信息系统…

数据库---PDO

以pikachu数据库为例&#xff0c;数据库名&#xff1a; pikachu 1.连接数据库 <?php $dsn mysql:hostlocalhost; port3306; dbnamepikachu; // 这里的空格比较敏感 $username root; $password root; try { $pdo new PDO($dsn, $username, $password); var_dump($pdo)…

零基础10 天入门 Web3之第1天

10 天入门 Web3 Web3 是互联网的下一代&#xff0c;它将使人们拥有自己的数据并控制自己的在线体验。Web3 基于区块链技术&#xff0c;该技术为安全、透明和可信的交易提供支持。我准备做一个 10 天的学习计划&#xff0c;可帮助大家入门 Web3&#xff1a; 想要一起探讨学习的…

C/C++ ③ —— C++11新特性

1. 类型推导 1.1 auto auto可以让编译器在编译期就推导出变量的类型 auto的使⽤必须⻢上初始化&#xff0c;否则⽆法推导出类型auto在⼀⾏定义多个变量时&#xff0c;各个变量的推导不能产⽣⼆义性&#xff0c;否则编译失败auto不能⽤作函数参数在类中auto不能⽤作⾮静态成员…

c++计算路过人间的天数

这个是从edge提供的界面想起来的。感觉我们也可以写一个。 于是就动手写起来了。 难点有一个就是获取当前的日期。 其他的就是简单的计算了。 分为三部分&#xff1a; 1、出生的闰年判断和当年的闰年判断&#xff1b; 2、出生的那一年到年底有多少天&#xff0c;今年进行了…

浅谈C语言编译与链接

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 翻译环境和运行环境 在ANSI C&#xff08;标准 C&#xff09;的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个…

如何快速搭建一个ELK环境?

前言 ELK是Elasticsearch、Logstash和Kibana三个开源软件的统称&#xff0c;通常配合使用&#xff0c;并且都先后归于Elastic.co企业名下&#xff0c;故被简称为ELK协议栈。 Elasticsearch是一个实时的分布式搜索和分析引擎&#xff0c;它可以用于全文搜索、结构化搜索以及分…

C/C++语言学习路线: 嵌入式开发、底层软件、操作系统方向(持续更新)

初级&#xff1a;用好手上的锤子 1 【感性】认识 C 系编程语言开发调试过程 1.1 视频教程点到为止 1.2 炫技视频看看就行 1.3 编程游戏不玩也罢 有些游戏的主题任务就是编程&#xff0c;游戏和实际应用环境有一定差异&#xff08;工具、操作流程&#xff09;&#xff0c;在…

ROS2高效学习第十章 -- ros2 高级组件之 component 合并进程启动 其一

ros2 高级组件之 component 合并进程启动 1 前言和资料2 正文2.1 component 引入2.2 component_demo 3 总结 1 前言和资料 第十章我们将学习 ros2 的多个高级组件&#xff0c;包括 component&#xff0c;复杂 launch 文件&#xff0c;TF2&#xff0c;gazebo 和 rviz。本文我们…

鱼眼相机的测距流程及误差分析[像素坐标系到空间一点以及测距和误差分析]

由于最近在整理单目测距的内容&#xff0c;顺手也总结下鱼眼相机的测距流程和误差分析&#xff0c;如果有错误&#xff0c;还请不吝赐教。 参考链接: 鱼眼镜头的成像原理到畸变矫正&#xff08;完整版&#xff09; 相机模型总结&#xff08;针孔、鱼眼、全景&#xff09; 三维…

Linux安装redis(基于CentOS系统,Ubuntu也可参考)

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a;wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a;tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…

深入了解高压电阻器的世界,探索其操作、类型和在各种高压应用中的关键作用

高压电阻器是高压条件下的专用元件&#xff0c;对于管理电压和散热至关重要 它们的工作原理是欧姆定律 类型包括线绕电阻、碳复合电阻、金属氧化物膜电阻、厚膜电阻和薄膜电阻这些电阻器在电力系统、医疗设备、汽车电子和电信设备中是必不可少的。 额定电压从600V到48KV 80p…

农村分散式生活污水分质处理及循环利用技术指南

标准已完成意见征集&#xff1a; 本文件给出了农村分散式生活污水分质处理及循环利用的总则、污水收集、污水分质处理、资源化利用、利用模式、运维管理等的指导。 本文件适用于农村分散式生活污水分质处理及循环利用的设施新建、扩建和改建工程的设计、施工与运维。 注:本文件…

Apache HBase(二)

目录 一、Apache HBase 1、HBase Shell操作 1.1、DDL创建修改表格 1、创建命名空间和表格 2、查看表格 3、修改表 4、删除表 1.2、DML写入读取数据 1、写入数据 2、读取数据 3、删除数据 2、大数据软件启动 一、Apache HBase 1、HBase Shell操作 先启动HBase。再…