当前位置: 首页 > news >正文

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分别是HandlerMappingHandlerAdapter的实现类:

        在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也是HandlerMappingHandlerAdapter的实现类:

  • 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中&#xff0c;doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理&#xff1a; 在捕获到异常后没有立刻进行处理&#xff0c;而是先用一个局部变量dispatchException进行记录&#xff0c;然后统一由…...

根据token获取了username后,能否在其他地方使用这个获取的username,或者在其他地方如何获取username?

当然可以在其他地方使用获取到的用户名。一旦你从token中获取到用户名&#xff0c;你可以将其存储在能够在整个应用程序中访问的地方。 在你的代码中&#xff0c;你从token中获取用户名的地方是这里&#xff1a; String username getUsernameFromToken(token);在这行之后&am…...

值模板参数Value Template Parameters

模板通常使用类型作为参数&#xff0c;但它们也可以使用值。使用类型和可选名称声明一个值模板参数&#xff0c;方式与声明函数参数类似。值模板参数仅限于可以指定编译时常量的类型是bool、char、int等&#xff0c;但不允许使用浮点类型、字符串字面值和类。 #include <io…...

Splashtop 荣获 TrustRadius 颁发的“2024年度最受欢迎奖”

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

使用python将`.mat`文件转换成`.xlsx`格式的Excel文件!!

要将.mat文件转换成.xlsx格式的Excel文件 第一步&#xff1a;导入必要的库第二步&#xff1a;定义函数来转换.mat文件第三步&#xff1a;调用函数注意事项 要将.mat文件转换成.xlsx格式的Excel文件&#xff0c;并保持文件名一致&#xff0c;你可以使用scipy.io.loadmat来读取.m…...

python基础 面向练习学习python1

python基础 面向练习学习python1 1. 电话查询问题描述1. 问题分析1. 输入输出分析2. 需求分析&#xff1a;将题目的数据存储并查询2. 所需知识&#xff1a; python 数据存储的类型3. 确定数据存储类型4. 如何书写代码拓展 从键盘中添加或删除联系人5. 回到数据查询 代码拓展 功…...

Ubuntu安装Docker和Docker Compose

文章目录 Docker安装Docker Compose安装示例前端Dockerfile示例 Docker官网&#xff1a; https://docs.docker.com/ Docker镜像仓库&#xff1a; https://hub.docker.com/ Docker安装 安装curl(可选) 如果已经安装了curl&#xff0c;则跳过此步骤 # 更新包缓存 sudo apt u…...

【linux软件基础知识】-死锁问题

死锁问题 当两个或多个线程由于每个线程都在等待另一个线程持有的资源而无法继续时,就会发生死锁 如下图所示, 在线程 1 中,代码持有了 L1 上的锁,然后尝试获取 L2 上的锁。 在线程 2 中,代码持有了 L2 上的锁,然后尝试获取 L1 上的锁。 在这种情况下,线程 1 已获取 L…...

C#面:简要谈对微软.NET 构架下 remoting 和 webservice 两项技术的理解以及实际中的应用

在微软 .NET 框架下&#xff0c;Remoting 和 WebService 是两种常用的技术&#xff0c;用于实现分布式应用程序的通信和交互。 Remoting&#xff08;远程调用&#xff09;&#xff1a; Remoting是一种用于在不同应用程序域之间进行通信的技术。它允许对象在不同的进程或计算机…...

《21天学通C++》(第十九章)STL集合类(set和multiset)

为什么需要set和multiset: 1.自动排序&#xff1a; set和multiset会自动按照元素的值进行排序。 2.快速查找&#xff1a; 由于元素是有序的&#xff0c;set和multiset可以提供对元素的快速查找&#xff0c;通常是基于二叉搜索树实现的&#xff0c;查找操作的时间复杂度为O(log …...

CSDN上是不是有机器人点赞和收藏?

我在CSDN上写作&#xff0c;主要是本来是记录学习工作中的一些知识点&#xff0c;看得人不多本来就能预想到的。 但是今天发现五一写的一篇博客&#xff0c;出现了很奇怪的阅读、点赞、收藏数。只有2个人阅读&#xff0c;但是有8个点赞&#xff0c;还有5个收藏。 我不禁怀疑CS…...

头歌C语言课程实验(递归函数、嵌套函数)

第1关&#xff1a;递归求阶乘数列 任务描述 题目描述&#xff1a;用递归求Sn1!2!3!4!5!…n!之值&#xff0c;其中n是一个数字。 相关知识&#xff08;略&#xff09; 编程要求 请仔细阅读右侧代码&#xff0c;结合相关知识&#xff0c;在Begin-End区域内进行代码补充。 输…...

树莓派的几种登录方式、及登录失败解决方式

使用TF卡安装树莓派的系统后&#xff0c;可以通过编辑TF卡里的文件来设置和启用 “ VNC ” 、“ SSH ” 和 “ 串口 ” 功能。不过&#xff0c;在使用中打开VNC和SSH可能并不直观或方便&#xff0c;因为这些服务通常在树莓派的系统内部配置和启动。但你可以通过以下步骤来设置和…...

数据库中视图的知识点

视图&#xff08;子查询&#xff09;&#xff1a;是从一个或多个表导出的虚拟的表&#xff0c;其内容由查询定义。具有普通表的结构&#xff0c;但是不实现数据存储。对视图的修改&#xff1a;单表视图一般用于查询和修改&#xff0c;会改变基本表的数据&#xff0c;多表视图一…...

aardio封装库) 微软开源的js引擎(ChakraCore)

前言 做爬虫肯定少不了JavaScript引擎的使用&#xff0c;比如在Python中现在一般用pyexecjs2来执行JavaScript代码&#xff0c;另外还有一些其他执行JavaScript的库&#xff1a; https://github.com/eight04/node_vm2: rpc调用nodejs&#xff0c;需要安装nodehttps://github.…...

LeetCode-hot100题解—Day6

原题链接&#xff1a;力扣热题-HOT100 我把刷题的顺序调整了一下&#xff0c;所以可以根据题号进行参考&#xff0c;题号和力扣上时对应的&#xff0c;那么接下来就开始刷题之旅吧~ 1-8题见LeetCode-hot100题解—Day1 9-16题见LeetCode-hot100题解—Day2 17-24题见LeetCode-hot…...

【Linux】gcc/g++的使用

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

2024-5-3学习笔记 虚拟继承原理

目录 原理 总结 前面提到过&#xff0c;解决菱形继承产生的数据二义性问题和数据冗余&#xff0c;就需要用到虚拟继承&#xff0c;关于它是如何解决的&#xff0c;我们来一起研究。 class Person { public :string _name ; // 姓名 }; class Student : virtual public Perso…...

C语言什么是“野指针”?

一、问题 “野指针”是⼀个⽐较陌⽣的术语&#xff0c;那么它到底是什么呢&#xff1f; 二、解答 当程序⾥声明了⼀个指针⽽又没有给这个指针赋值&#xff0c;使其指向⼀个地址时&#xff0c;这样的指针就称为“野指针”。 “野指针”会随意地指向⼀个地址。当对这个指针进⾏操…...

LeetCode--所有质数、质数对

1.0 Q: 输出 100 以内所有质数 1.1 /* 第一层循环控制检查到哪个数* 第二层通过遍历除以每个比他小的数的方式,检查每个数是不是质数* 由于要遍历检查,设置一个标记,只要任意一次循环可以整除,我们就设置该标记为不是质数 */boolean isPrime true;for (int i 2; i < 100…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

现代密码学 | 椭圆曲线密码学—附py代码

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

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...

Java并发编程实战 Day 11:并发设计模式

【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天&#xff0c;今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案&#xff0c;它们不仅提供了优雅的设计思路&#xff0c;还能显著提升系统的性能…...