spring高级篇(七)
1、异常处理
在DispatcherServlet中,doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理:
在捕获到异常后没有立刻进行处理,而是先用一个局部变量dispatchException进行记录,然后统一由processDispatchResult() 方法进行处理:
processDispatchResult() 方法中,首先判断异常是否为空,如果为空就不进行处理,然后判断是否是ModelAndViewDefiningException类型异常,如果不是就进入processHandlerException()
processHandlerException() 中,会循环遍历handlerExceptionResolvers集合去匹配并处理异常:
@Nullableprivate List<HandlerExceptionResolver> handlerExceptionResolvers;
HandlerExceptionResolver是一个接口,我们使用ExceptionHandlerExceptionResolver的实现去模拟异常处理的过程:
ExceptionHandlerExceptionResolver专门用于解析 @ExceptionHandler注解,把标注了 @ExceptionHandler注解的方法作为处理异常的方法
//专门用于解析 @ExceptionHandler注解,把标注了 @ExceptionHandler注解的方法作为处理异常的方法
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
//设置消息转换json
resolver.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
//初始化 加入常用的参数和返回值解析器
resolver.afterPropertiesSet();testJSON(resolver);
在afterPropertiesSet() 初始化方法中,已经预先定义好了一些参数解析器和返回值处理器:
定义一个控制器:
public class Controller1 {public void foo(){}/*** 处理异常的方法,并且将返回值转成JSON* @param e* @return*/@ExceptionHandler@ResponseBodypublic Map<String,Object> handle(ArithmeticException e){return Collections.singletonMap("error",e.getMessage());}
}
resolver.resolveException()方法会检查Controller1中是否有@ExceptionHandler注解标注的方法,如果有,并且参数的异常和实际发生的异常能对应上,就执行其中的逻辑:
private static void testJSON(ExceptionHandlerExceptionResolver resolver) throws NoSuchMethodException {MockHttpServletRequest request = new MockHttpServletRequest();MockHttpServletResponse response = new MockHttpServletResponse();//将控制器的foo方法封装成HandlerMethod对象HandlerMethod handlerMethod = new HandlerMethod(new Controller1(),Controller1.class.getMethod("foo"));//检查Controller1中是否有@ExceptionHandler注解标注的方法,如果有,并且参数的异常和实际发生的异常能对应上,就执行其中的逻辑resolver.resolveException(request,response,handlerMethod,new ArithmeticException("数学异常"));System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));}
此外处理异常的方法还支持ModelAndView类型的返回,与上述解析异常的过程相似。
我们还可以转发自定义的错误处理页面:
/*** 转发到自定义的错误页面* @return*/@Beanpublic ErrorPageRegistrar errorPageRegistrar(){return new ErrorPageRegistrar() {@Overridepublic void registerErrorPages(ErrorPageRegistry registry) {registry.addErrorPages(new ErrorPage("/error"));}};}/*** 注册后处理器* @return*/@Beanpublic ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor(){return new ErrorPageRegistrarBeanPostProcessor();}/*** Spring Boot中配置自定义的BasicErrorController,用于处理基本的错误页面和错误信息。* @return*/@Beanpublic BasicErrorController basicErrorController(){ErrorProperties errorProperties = new ErrorProperties();errorProperties.setIncludeException(true);return new BasicErrorController(new DefaultErrorAttributes(),errorProperties);}
2、BeanNameUrlHandlerMapping&SimpleControllerHandlerAdapter
BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter分别是HandlerMapping和HandlerAdapter的实现类:
在BeanNameUrlHandlerMapping中,以/开头的 bean 的名字会被当作映射路径。这些 bean 本身当作 handler,要求实现 Controller 接口。
准备一个Config类,将BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter注册成bean:
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class})//将配置文件中的属性绑定到对象中
public class Config {/*** 注册内嵌web容器工厂 tomcat容器*/@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(){return new TomcatServletWebServerFactory();}/*** 创建DispatcherServlet* 首次使用时,由tomcat容器初始化* @return*/@Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}/*** 注册DispatcherServlet springmvc入口* @param dispatcherServlet* @return*/@Beanpublic DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties){DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");//设置tomcat容器启动时即进行DispatcherServlet初始化registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());return registrationBean;}@Beanpublic BeanNameUrlHandlerMapping beanNameUrlHandlerMapping(){return new BeanNameUrlHandlerMapping();}@Beanpublic SimpleControllerHandlerAdapter simpleControllerHandlerAdapter(){return new SimpleControllerHandlerAdapter();}@Bean("/c3")public Controller controller3(){return (request, response) -> {response.getWriter().print("this is c3");return null;};}}
再准备两个实现了Controller接口的控制器类:
@Component("/c1")
public class Controller1 implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {response.getWriter().print("this is c1");return null;}
}
@Component("c2")
public class Controller2 implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {response.getWriter().print("this is c2");return null;}
}
启动主类:
public class A31 {public static void main(String[] args) {AnnotationConfigServletWebServerApplicationContext context =new AnnotationConfigServletWebServerApplicationContext(Config.class);}
}
我们可以模拟实现这一组映射器和适配器:
定义一个类实现BeanNameUrlHandlerMapping的顶级接口HandlerMapping:
它的作用是在初始化时收集容器中所有以/开头的路径和类成map集合,并且在调用时会判断当前requestURI能否与map集合中的任意元素相匹配:
(复习一下,容器初始化时会收集所有 @RequestMapping 映射信息,封装为 Map)
/*** 模拟处理器映射器* 收集请求中以/开头的bean*/
@Component
public class MyHandlerMapping implements HandlerMapping {/*** 处理器映射器,getHandlerMethods中 和当前requestURI 匹配的路径信息* @param request* @return* @throws Exception*/@Overridepublic HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {String requestURI = request.getRequestURI();Controller controller = map.get(requestURI);//匹配不上 404if (controller == null){return null;}return new HandlerExecutionChain(controller);}@Autowiredprivate ApplicationContext applicationContext;private Map<String, Controller> map;/*** 初始化时收集所有容器中/开头的bean信息*/@PostConstructpublic void init() {Map<String, Controller> beansOfType = applicationContext.getBeansOfType(Controller.class);map = beansOfType.entrySet().stream().filter(e -> e.getKey().startsWith("/")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));System.out.println(map);}
}
定义一个类实现HandlerAdapter作为适配器,负责将请求分派给实现了controller接口类中的方法,和RequestMappingHandlerAdapter相比,不需要自定义参数和返回值处理器。
/*** 模拟处理器适配器*/
@Component
public class MyHandlerAdapter implements HandlerAdapter {/*** 判断传递的handler是否是当前MyHandlerAdapt支持的* @param handler* @return*/@Overridepublic boolean supports(Object handler) {return handler instanceof Controller;}/*** 调用实现了Controller接口的方法* 无需参数解析器,返回值处理器* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof Controller){((Controller) handler).handleRequest(request, response);}return null;}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {return -1;}
}
结论:Controller1和Controller3能匹配上,Controller2匹配不上(路径中没有/)
3、RouterFunctionMapping&HandlerFunctionAdapter
RouterFunctionMapping和HandlerFunctionAdapter也是HandlerMapping和HandlerAdapter的实现类:
- RouterFunctionMapping会收集所有的RouterFunction,请求到达时,根据条件找到HandlerFunction
- HandlerFunctionAdapter会调用符合条件的HandlerFunction。
/*** 会收集所有的RouterFunction* 请求到达时,根据条件找到HandlerFunction* @return*/@Beanpublic RouterFunctionMapping routerFunctionMapping(){return new RouterFunctionMapping();}/*** 调用符合条件的HandlerFunction* @return*/@Beanpublic HandlerFunctionAdapter handlerFunctionAdapter(){return new HandlerFunctionAdapter();}
RouterFunction分为两部分:匹配规则和具体的执行逻辑(请求是GET类型,并且路径是/r1,就执行new HandlerFunction<ServerResponse>()中的逻辑)
@Beanpublic RouterFunction<ServerResponse> r1(){//参数一 匹配规则 参数二 具体的执行逻辑return RouterFunctions.route(RequestPredicates.GET("/r1"), new HandlerFunction<ServerResponse>() {@Overridepublic ServerResponse handle(ServerRequest request) throws Exception {return ServerResponse.ok().body("this is r1");}});}
下一篇对Spring MVC 的执行流程做一个总结。
相关文章:

spring高级篇(七)
1、异常处理 在DispatcherServlet中,doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理: 在捕获到异常后没有立刻进行处理,而是先用一个局部变量dispatchException进行记录,然后统一由…...
根据token获取了username后,能否在其他地方使用这个获取的username,或者在其他地方如何获取username?
当然可以在其他地方使用获取到的用户名。一旦你从token中获取到用户名,你可以将其存储在能够在整个应用程序中访问的地方。 在你的代码中,你从token中获取用户名的地方是这里: String username getUsernameFromToken(token);在这行之后&am…...
值模板参数Value Template Parameters
模板通常使用类型作为参数,但它们也可以使用值。使用类型和可选名称声明一个值模板参数,方式与声明函数参数类似。值模板参数仅限于可以指定编译时常量的类型是bool、char、int等,但不允许使用浮点类型、字符串字面值和类。 #include <io…...

Splashtop 荣获 TrustRadius 颁发的“2024年度最受欢迎奖”
2024年5月8日 加利福尼亚州库比蒂诺 Splashtop 在全球远程访问和支持解决方案领域处于领先地位,该公司正式宣布将连续第三年荣获远程桌面和远程支持类别的“TrustRadius 最受欢迎奖”。Splashtop 的 trScore 评分高达8.6分(满分10分)&#x…...

使用python将`.mat`文件转换成`.xlsx`格式的Excel文件!!
要将.mat文件转换成.xlsx格式的Excel文件 第一步:导入必要的库第二步:定义函数来转换.mat文件第三步:调用函数注意事项 要将.mat文件转换成.xlsx格式的Excel文件,并保持文件名一致,你可以使用scipy.io.loadmat来读取.m…...
python基础 面向练习学习python1
python基础 面向练习学习python1 1. 电话查询问题描述1. 问题分析1. 输入输出分析2. 需求分析:将题目的数据存储并查询2. 所需知识: python 数据存储的类型3. 确定数据存储类型4. 如何书写代码拓展 从键盘中添加或删除联系人5. 回到数据查询 代码拓展 功…...
Ubuntu安装Docker和Docker Compose
文章目录 Docker安装Docker Compose安装示例前端Dockerfile示例 Docker官网: https://docs.docker.com/ Docker镜像仓库: https://hub.docker.com/ Docker安装 安装curl(可选) 如果已经安装了curl,则跳过此步骤 # 更新包缓存 sudo apt u…...

【linux软件基础知识】-死锁问题
死锁问题 当两个或多个线程由于每个线程都在等待另一个线程持有的资源而无法继续时,就会发生死锁 如下图所示, 在线程 1 中,代码持有了 L1 上的锁,然后尝试获取 L2 上的锁。 在线程 2 中,代码持有了 L2 上的锁,然后尝试获取 L1 上的锁。 在这种情况下,线程 1 已获取 L…...
C#面:简要谈对微软.NET 构架下 remoting 和 webservice 两项技术的理解以及实际中的应用
在微软 .NET 框架下,Remoting 和 WebService 是两种常用的技术,用于实现分布式应用程序的通信和交互。 Remoting(远程调用): Remoting是一种用于在不同应用程序域之间进行通信的技术。它允许对象在不同的进程或计算机…...
《21天学通C++》(第十九章)STL集合类(set和multiset)
为什么需要set和multiset: 1.自动排序: set和multiset会自动按照元素的值进行排序。 2.快速查找: 由于元素是有序的,set和multiset可以提供对元素的快速查找,通常是基于二叉搜索树实现的,查找操作的时间复杂度为O(log …...

CSDN上是不是有机器人点赞和收藏?
我在CSDN上写作,主要是本来是记录学习工作中的一些知识点,看得人不多本来就能预想到的。 但是今天发现五一写的一篇博客,出现了很奇怪的阅读、点赞、收藏数。只有2个人阅读,但是有8个点赞,还有5个收藏。 我不禁怀疑CS…...
头歌C语言课程实验(递归函数、嵌套函数)
第1关:递归求阶乘数列 任务描述 题目描述:用递归求Sn1!2!3!4!5!…n!之值,其中n是一个数字。 相关知识(略) 编程要求 请仔细阅读右侧代码,结合相关知识,在Begin-End区域内进行代码补充。 输…...
树莓派的几种登录方式、及登录失败解决方式
使用TF卡安装树莓派的系统后,可以通过编辑TF卡里的文件来设置和启用 “ VNC ” 、“ SSH ” 和 “ 串口 ” 功能。不过,在使用中打开VNC和SSH可能并不直观或方便,因为这些服务通常在树莓派的系统内部配置和启动。但你可以通过以下步骤来设置和…...
数据库中视图的知识点
视图(子查询):是从一个或多个表导出的虚拟的表,其内容由查询定义。具有普通表的结构,但是不实现数据存储。对视图的修改:单表视图一般用于查询和修改,会改变基本表的数据,多表视图一…...

aardio封装库) 微软开源的js引擎(ChakraCore)
前言 做爬虫肯定少不了JavaScript引擎的使用,比如在Python中现在一般用pyexecjs2来执行JavaScript代码,另外还有一些其他执行JavaScript的库: https://github.com/eight04/node_vm2: rpc调用nodejs,需要安装nodehttps://github.…...
LeetCode-hot100题解—Day6
原题链接:力扣热题-HOT100 我把刷题的顺序调整了一下,所以可以根据题号进行参考,题号和力扣上时对应的,那么接下来就开始刷题之旅吧~ 1-8题见LeetCode-hot100题解—Day1 9-16题见LeetCode-hot100题解—Day2 17-24题见LeetCode-hot…...

【Linux】gcc/g++的使用
🎉博主首页: 有趣的中国人 🎉专栏首页: Linux 🎉其它专栏: C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好,本片文章将会讲解Linux中gcc/g使用的相关内容。 如果看到最后您觉得这篇文章写得不错…...

2024-5-3学习笔记 虚拟继承原理
目录 原理 总结 前面提到过,解决菱形继承产生的数据二义性问题和数据冗余,就需要用到虚拟继承,关于它是如何解决的,我们来一起研究。 class Person { public :string _name ; // 姓名 }; class Student : virtual public Perso…...
C语言什么是“野指针”?
一、问题 “野指针”是⼀个⽐较陌⽣的术语,那么它到底是什么呢? 二、解答 当程序⾥声明了⼀个指针⽽又没有给这个指针赋值,使其指向⼀个地址时,这样的指针就称为“野指针”。 “野指针”会随意地指向⼀个地址。当对这个指针进⾏操…...

LeetCode--所有质数、质数对
1.0 Q: 输出 100 以内所有质数 1.1 /* 第一层循环控制检查到哪个数* 第二层通过遍历除以每个比他小的数的方式,检查每个数是不是质数* 由于要遍历检查,设置一个标记,只要任意一次循环可以整除,我们就设置该标记为不是质数 */boolean isPrime true;for (int i 2; i < 100…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...