【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】AOP(2)
文章目录
- 【JavaEE】AOP(2)
- 1. 统一登录校验处理
- 1.1 自定义拦截器
- 1.2 将自定义拦截器加入到系统配置
- 1.3 测试
- 1.4 对于静态资源的处理
- 1.5 小练习:统一登录拦截处理
- 1.6 拦截器原理
- 1.6.1 执行流程
- 1.6.2 源码分析
- 1.7 扩展:统一访问前缀添加
- 2. 统一异常处理
- 2.1 为什么需要异常处理
- 2.2 应用异常处理器
- 3. 统一数据格式返回处理
- 3.1 为什么要统一数据格式
- 3.2 数据格式统一方法
- 3.3 应用数据格式返回统一处理器
- 3.4 String类型无法处理的问题
- 3.5 @ControllerAdvice 源码分析
【JavaEE】AOP(2)
在前面的Spring AOP的学习之中,Spring AOP去实现AOP,虽然比较灵活,可以实现很多想法,但是也有一些现实的问题:
- 没办法获取到HttpRequest,一些功能难以实现
- 进而无法获取HttpSession对象,这样登录校验功能就无法实现
- 我们要对⼀部分方法进行拦截,而另⼀部分方法不拦截,如注册方法和登录方法是不拦截的,这样的话排除方法的规则很难定义,甚至没办法定义
我们手动去实现,太难了!
所以本文讲解的重点就是,Spring全家桶实现AOP的几种固定的机制,虽然较不灵活,但是实现的功能有针对性,且代码简单!
- 只要我们按照框架的要求去写,就可以实现对应的功能
- 这些功能都是很常见、很必要的功能!
- 所以不要纠结于这个框架不够全面,不能实现一些极端特殊的要求,因为它只是 把一切常见的功能,打包成现成的模块 ,方便开发;如果有其他特殊要求,用Spring AOP等等偏底层地去实现就好了!
- 框架就是这样的,方便一些常见开发的操作,我们不需要过多了解底层实现,“坐享其成”即可😀
- Spring全家桶实现AOP更偏向于具体的统一处理功能,而Spring AOP实现AOP更偏底层
网路资料:
Spring AOP是Spring框架中的一个模块,用于实现面向切面编程。它通过动态代理或者字节码生成的方式,在运行时将横切逻辑织入到目标对象的方法中,从而在目标对象执行前、执行后或者执行异常时执行额外的操作。Spring AOP主要基于代理模式来实现。
Spring Boot框架是基于Spring框架的扩展,用于简化和加速Spring应用程序的开发。
Spring Boot并没有重新实现AOP概念,而是直接继承了Spring框架中的AOP模块,因此在Spring Boot中,使用AOP的方式和Spring框架中的方式是一样的。Spring Boot提供了更方便的自动配置和默认约定,使得使用AOP更加简单。
因此,Spring AOP和Spring Boot框架实现的AOP的本质是相同的,都是通过代理方式实现的面向切面编程。区别主要在于Spring全家桶提供了[更简化的配置]和[默认约定],使得使用AOP更加方便。
- 因为代码的运行就是通过 Spring Boot框架来运行的,以此来控制一些逻辑也很合理!
本文讲解的几个统一处理:
- 统一登录校验处理
- Spring 拦截器
- 统一异常处理
- 统一数据格式返回处理
没学到不代表不重要,需要用到什么统一处理的功能,再专门去学!
1. 统一登录校验处理
框架的学习说到底就是学习一个约定,按照约定才能实现方便功能~
Spring全家桶提供了具体的现成拦截器:HandlerInterceptor ,应用拦截器分为两步:
-
创建自定义拦截器,实现 HandlerInterceptor接口,重写 preHandle方法
-
preHandle()
=> 目标方法执行之前要执行的预处理方法 -
其返回值为true/false
-
true=>目标方法可执行,放行
-
false=>目标方法不可执行,拦截
//伪代码 HandlerInterceptor o = new LoginInterceptor(); if(!o.preHandle(...)) {return; } targetMethod();
-
-
-
重写 WebMvcConfigurer 的
addInterceptors(InterceptorRegistry registry)
方法- 添加拦截器到registry中(登记)
- 可以添加多个拦截器,按顺序进行拦截(如果逻辑冲突,按照顺序,以第一个结果为准)
- 添加拦截的路由/排除拦截的路由
- 添加拦截器到registry中(登记)
1.1 自定义拦截器
Ctrl O重写方法:
默认返回true,放行
Java对于一些常量通常会放在一个类里面,以访问的形式去获取常量,而不是每次都手敲:
1.2 将自定义拦截器加入到系统配置
Ctrl O重写方法:
- 加Configuration!否则项目启动不会加载这个类,自然不会进行系统配置
- 实现MebMvcConfigurer,自然就是跟Spring Mvc(网络相关)有关的配置
- 不仅仅只有配置文件才能配置,配置类也可以哦!
拦截器对象加入到Spring容器里
一般写成这样:
PathPatterns,路径的样式
其实直接new一个咱们的拦截器对象甚至匿名内部类重写那个方法也行,因为这个参数只是定义预处理方法
这个类只是游离在外的普通类,一个拦截器的定义罢了~
为什么可以怎么写:
- 其实就是因为每个加或者减后返回的都是同一个对象~
1.3 测试
为了突出结果,做出以下改动:
- 拦截成功跳转百度:
- 提示执行了拦截器
- 排除路由/test/say_hello
效果:
1.4 对于静态资源的处理
比如图片/html/js/css等资源,如果是服务器的,都是向服务器请求来的
-
html等等很常规无非就是:
.excludePathPatterns("/**/*.html")
-
但是图片不一样,图片的格式很多很多,我们没办法一一列举,而我们可以这么做:
- 将图片保存到一个目录image里,然后排除整个image目录
.excludePathPatterns("/image/**")
1.5 小练习:统一登录拦截处理
将我们之前做的博客前端资源拷贝到static里:
后端基本上代码不变,主要是对静态资源和一些路由的拦截排除:
有可能有缓存的问题,刚才的资源没有出现在target中,所以要删掉重新启动
效果:
重定向只是 对应的路径 相当于重定向后的路径,所以如果只是请求资源或者请求响应数据,不是访问 对应的路径 的网页,是不会触发跳转的~
- 只会表现为访问不到资源/响应状态码为302
例如:
- 背景消失~
补充,静态资源以static为起始,接受多级目录访问:
注意删除target重新启动!
1.6 拦截器原理
1.6.1 执行流程
之前:
现在:
- 就在请求的传递路上"截胡"
1.6.2 源码分析
所有的Controller执行都会通过一个调度器DispatcherServlet来实现,可以通过控制台的日志观察到:(前提是拦截器工作了!)
双击shift查找DispatcherServlet:
- 这个就是我们重写的方法!
- 循环拦截器集合(多个拦截器的情况)
true就可以继续进行后面的程序(我们的目标方法就在后面)
false就直接return(响应通过输出型参数response返回)
- 客户端发送请求后,在Spring MVC框架中会经过一系列的组件处理,其中就有可能被拦截器 “截胡”
1.7 扩展:统一访问前缀添加
所有请求地址添加api前缀:
@Configuration//不是Configurable!
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("api", c -> true);}
}
- api,原有路由多一级
- 但是不代表默认不被拦截!
- 还是要去排除/api/…
- 还会导致原有路由不能用
- c -> true,是一个表达式(不需要了解,感兴趣可以了解),表示启动前缀,false则无效果
2. 统一异常处理
2.1 为什么需要异常处理
如果后端报异常:
(监视器已关掉,此处重点观察异常)
信息太过杂,我们需要总结要点,但是不能边开发边写这些异常处理操作,而且几个开发者写的可能都不一样,后期也不方便维护,所以要统一处理~
所以我们希望异常返回的时候应该有明确统一的信息!
网路资料(了解):
错误处理的一致性:通过统一处理异常,可以确保应用程序在遇到异常时采取一致的处理方式。这样可以提供更好的用户体验,避免不一致的错误展示或处理方式。
代码的可维护性:通过将异常处理逻辑集中到一个地方,可以提高代码的可维护性。如果每个控制器方法都有自己的异常处理逻辑,会导致代码重复和冗余,难以维护和更新。
封装敏感信息:统一处理异常可以帮助我们封装敏感信息,防止泄露给客户端。在异常处理过程中,我们可以选择性地披露或隐藏敏感信息,并返回友好的错误消息给客户端。
日志记录和监控:通过统一处理异常,我们可以方便地进行日志记录和监控。在异常处理过程中,我们可以记录异常的详细信息,包括异常类型、堆栈跟踪等,以便后续的故障排查和系统监控。
异常转换和包装:有时候,我们可能需要将底层的异常转换为更高级别的异常,或者将多个异常包装为一个更通用的异常。通过统一处理异常,可以方便地进行这些异常转换和包装操作,以便更好地传递和处理异常。
错误页面的统一管理:通过统一处理异常,可以将错误页面的管理集中到一个地方。这样可以更方便地自定义错误页面,并确保所有的异常都有对应的错误展示界面。
总之,通过统一处理异常,我们可以提高代码的可维护性和可读性,封装敏感信息,方便日志记录和监控,进行异常转换和包装,以及统一管理错误页面。这些都有助于提升应用程序的质量和用户体验。
大概了解一下就行了,实际项目遇到去使用体验会更好!
2.2 应用异常处理器
我们仍然有现成的异常处理器可以用,如何应用:
- @ControllerAdvice
- 加在类上,代表其为控制器通知类此类会“监听”后端环境,随着项目启动的加载,如果抛出异常会被此类感知到
- @ExceptionHandler(Exception.class)
- 顾名思义,这就是异常处理器,加在方法上,其返回值就是格式化后的数据,注解的括号内应该为对应异常的类对象,并且这个方法可以有一个参数e(名字自取),e会被注入刚才的异常对象
- 会在获取到异常后调用这个方法,(AOP advice的位置)
补充:
返回的格式以json为最佳,所以返回的方式有两种:
- 自定义对象/自定义对象集合
- Map对象,自定义映射关系
- 方便,以这个为例
返回值的类型可以是Object,包括其泛型或者其属性,系统会自动去获取每一个都是谁向上转型而来的
- 无论什么异常都会被这个异常处理器捕捉到,因为Exception是所有异常的父类,而参数e则是被向上转型来的,所以其getMessage方法被重写了,仍然可以正常使用
- 随便写的,只是示例!
但是对于潜在的个别异常,如NullPointerException,应该单独处理,并且被子类捕获就不会被父类捕获了~
- 因为异常太多了,所以还是需要Exception这个"老父亲"来兜底
效果:
其他异常效果:
3. 统一数据格式返回处理
3.1 为什么要统一数据格式
其实每个公司都会对字段名严格必须符合规范,但是返回的格式则千奇百怪
统一数据格式的优点很多,比如以下几个:
- 方便前后端程序员对接数据
- 降低前后端沟通成本,所有接口都按一个格式返回
- 有利于项目统一数据的维护和修改
- 有利于后端技术部门的统一规范,不会出现稀奇古怪的返回内容
3.2 数据格式统一方法
一样可以按照刚才的两种方式:
- 自定义对象
- 本次演示这种,因为有个小坑
- Map
现在我们分为两种情况:
- 成功是一个方法
- 失败是一个方法
3.3 应用数据格式返回统一处理器
我们仍然有现成的数据格式返回统一处理器可以用,如何应用:
- @ControllerAdvice
- 加在类上,代表其为控制器通知类此类会“监听”后端环境,随着项目启动的加载,如果有返回值就会被捕获到!
- 实现接口ResponseBodyAdvice
- 重写supports方法,表示我们是否要重写beforeBodyWrite方法
- 通过这个方法以及参数我们可以动态的选择性的对一些控制器和方法不重写/重写
- return true代表可以重写,反之不能重写
- 重写beforeBodyWrite方法
- 顾名思义就是“篡改”返回值,这个方法会在方法返回之前调用,替原方法返回值,(AOP advice的位置)
- 默认返回像加了@ResponseBody
- 重写supports方法,表示我们是否要重写beforeBodyWrite方法
测试:
- 返回值类型已经不重要了,因为都会被返回值统一处理掉
- 当然写原来的类型会更好,因为这样有利于代码的正确性,并且此接口可能被处理器排除了~
效果:
3.4 String类型无法处理的问题
- 因为异常处理,把异常处理的信息再次看成返回值了,实际上返回值处理器运行了两次🤣
因为框架在对返回数据格式处理的时候分为两类:
- body是非String类型向上转型而来
- 跟原来一样,返回值Result类型可以直接返回,框架自动转换为json字符串
- body是String类型向上转型而来
- 也跟原来一样,应该返回String类型并且视作html代码,Result类型的返回值尝试转换为String类型,但是Result类型无法直接转换为String类型!
- 因为框架【并没有】将它看成非String对象返回,然后按非String对象的方式转换为json字符串!
- 然后把Result对象看成String对象返回,显然直接报异常!
- 证据就是,Result对象是完好的,只要调用toString方法返回,不会报异常,但是直接返回Result会报异常。说明返回值必须是字符串,并且最终会被渲染
所以我们应该对String类型单独处理:
效果:
在浏览器中,这段字符串被认为是html:
3.5 @ControllerAdvice 源码分析
跟其他类注解一样派生于@Component,而所有的组件都会调用InitializingBean接口:
Spring MVC中的RequestMappingHandlerAdapter,实现了它:
实现了afterPropertiesSet方法:
异常处理器和返回值处理器的核心前提就是有这个注解,原因归功于它的监听机制
- 我们发现,这些ControllerAdvice注解类Bean对象特殊地被查找和存储在容器之中,它们比较“忙”,当发生某个事件的时候,调用对应的Advice方法,比如统一异常处理,统一返回值格式处理…
文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!本文重点讲基本使用方法,具体功能实现要结合实际应变!
- 例如拦截后咋样,异常处理格式咋样,统一数据格式返回咋样…都不是固定的,是根据实际情况的!
代码:spring_boot_aop_demo/src/main/java/com/example/demo · 游离态/马拉圈2023年8月 - 码云 - 开源中国 (gitee.com)
相关文章:

【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】AOP(2) 文章目录 【JavaEE】AOP(2)1. 统一登录校验处理1.1 自定义拦截器1.2 将自定义拦截器加入到系统配置1.3 测试1.4 对于静态资源的处理1.5 小练习:统一登录拦截处理1.6 拦截器原理1.6.1 执行流程1.6.2 源…...

HQL解决连续三天登陆问题
1.背景 统计连续登录天数超过3天的用户,输出信息包括:用户id,登录天数,起始时间,结束时间; 2.准备数据 -- 建表 create table if not exists user_login_3days(user_id STRING,login_date date );--插入…...
(一)Docker简介(一篇足以)
一、简介 一个项目环境配置相当麻烦,如果换一台机器跑起来,所有配置就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环…...

RK3568 安卓源码编译
一.repo安卓编译工具 项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去,各模块各自管理自己的版本。Android源码引用了很多开源项目,每一个子项目都是一个Git仓库,每个Git仓库都有很多分支版本,为了方便统…...

第4篇:vscode+platformio搭建esp32 arduino开发环境
第1篇:Arduino与ESP32开发板的安装方法 第2篇:ESP32 helloword第一个程序示范点亮板载LED 第3篇:vscode搭建esp32 arduino开发环境 1.配置默认安装路径,安装到D盘。 打开环境变量,点击新建 输入变量名PLATFORMIO_CORE_DIR与路径:D:\PLATF…...

2023前端面试笔记 —— CSS3
系列文章目录 内容链接2023前端面试笔记HTML52023前端面试笔记CSS3 文章目录 系列文章目录前言一、CSS选择器的优先级二、通过 CSS 的哪些方式可以实现隐藏页面上的元素三、px、em、rem之间有什么区别?四、让元素水平居中的方法有哪些五、在 CSS 中有哪些定位方式六…...

iOS 如何对整张图分别局部磨砂,并完全贴合
官方磨砂方式 - (UIVisualEffectView *)effectView{if(!_effectView){UIBlurEffect *blur [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];_effectView [[UIVisualEffectView alloc] initWithEffect:blur];}return _effectView; }使用这种方式对一张图的上半部分和…...

Packet_Tracer的使用
一、实验目的: 通过该实验了解Packet Tracer的使用方法,能够用Packet Tracer建立和模拟网络模型。 二、主要任务: 1.熟悉PT的界面,了解按键用途。 2.尝试自己建立一个小型网络,并测试连通性。 3.学习P…...
WPF如果未定义绑定的属性,程序如何处理
问题:wpf中,<Button IsEnabled"{Binding IsValid1}"></Button>,如果没定义绑定的属性IsValid1,可以正常用吗 解答:在 WPF 中,如果没有定义绑定的属性 IsValid1,会导致绑…...
韩国留学生生活之-租房篇,柯桥韩语培训留学韩语需要学到什么程度
对于计划在韩国留学的人来说,找到合适的租房是一个重要而且有挑战性的任务。 留学生遇到的常见租房类型为月付型、全税房。月付型就是我们常见的租房方式,一般都需要支付一个月或数个月月租的押金,按时间付房租即可,租期通常为一…...
论文笔记:基于概念漂移的在线类非平衡学习系统研究
0 摘要 论文:A Systematic Study of Online Class Imbalance Learning With Concept Drift 发表:2018年发表在TNNLS上 源代码:? 作为一个新兴的研究课题,在线类非平衡学习往往结合了类非平衡和概念漂移的挑战。它处理…...
ubuntu22.04下rv1109 rootfs编译问题处理
ubuntu22.04下rv1109 rootfs编译问题处理 buildroot编译出错记录问题一:c-stack.c的SIGSTKSZ错误解决办法问题二:libfakeroot.c的_STAT_VER报错解决办法问题三:fwriter_buffer重复定义解决办法问题四: qfloat16.h报错解决办法问题…...
Spring Boot Dubbo Zookeeper
文章目录 Spring Boot Dubbo Zookeeper简介DubboCommonProviderConsumer Zookeeper Spring Boot Dubbo Zookeeper 简介 Dubbo Common 公共依赖 <!-- Spring Boot Starter --> <dependency><groupId>org.springframework.boot</groupId><artifac…...

线程池的概念及实现原理
本篇是对前面线程池具体实现过程的补充,实现过程可参考 线程池的实现全过程v1.0版本(手把手创建,看完必掌握!!!)_竹烟淮雨的博客-CSDN博客 线程池的实现v2.0(可伸缩线程池…...

iOS App逆向之:iOS应用砸壳技术
在iOS逆向,有一项关键的技术叫做“iOS砸壳”(iOS App Decryption)。自iOS 5版本以来,苹果引入了应用程序加密机制,使得大部分应用都需要进行砸壳操作才能进行逆向分析。因此作为开发者、逆向工程师和安全研究人员都需要…...
【高性能计算】opencl安装及相关概念
目录 从异构计算讲起opencl安装的相关说明查看linux系统cpu及gpu型号方法安装opencl helloword程序运行 从异构计算讲起 异构计算是一种利用多种不同类型的计算资源来协同解决计算问题的方法。它的核心思想是将不同特性和能力的计算设备(例如CPU、GPU、FPGA等&…...

盛最多水的容器——力扣11
int maxArea(vector<int>& height) {int l=0, r=height.size()...

2023年高教社杯数学建模思路 - 复盘:校园消费行为分析
文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…...

Flink_state 的优化与 remote_state 的探索
摘要:本文整理自 bilibili 资深开发工程师张杨,在 Flink Forward Asia 2022 核心技术专场的分享。本篇内容主要分为四个部分: 相关背景state 压缩优化Remote state 探索未来规划 点击查看原文视频 & 演讲PPT 一、相关背景 1.1 业务概况 从…...

Kdab QML (part9)自由缩放时钟
文章目录 Kdab QML (part9)自由缩放时钟代码详细解释运行截图 Kdab QML (part9)自由缩放时钟 代码 import QtQuick 2.15 import QtQuick.Window 2.15Window {id: rootwidth: 500height: 500visible: truecolor: "lightgrey"title: qsTr("Hello World")It…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...

数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...

uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)
引言 在嵌入式系统中,用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例,介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单,执行相应操作,并提供平滑的滚动动画效果。 本文设计了一个…...