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 的执行流程做一个总结。

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

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

相关文章

VMware虚拟机中ubuntu使用记录(5)—— 如何在ubuntu中安装USB相机ros驱动并获取usb摄像头数据

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、ROS下USB相机驱动1.准备工作(1) 下载驱动(2) 创建ROS工作空间 2. 安装usb_cam驱动(1) 安装usb_cam驱动包(2) 编译代码 3. 修改usb_cam驱动的配置文件(1) 查看US…

掌握TypeScript的非空断言(!)和可选链(?):开发效率翻倍!

引言 标题&#xff1a;掌握TypeScript的非空断言和可选链&#xff1a;开发效率翻倍&#xff01;简短介绍&#xff1a;在TypeScript中&#xff0c;?和!操作符是提高代码安全性和开发效率的强大工具。本文将为你揭示它们的使用方式和最佳实践。 背景知识 易于理解的解释&…

vue2+@tinymce/tinymce-vue富文本编辑器的实现

1.使用的第三方的库 npm install @tinymce/tinymce-vue@3.2.8 tinymce@5.10.9 --save 2.引入应用,直接上代码,支持上传图片,视频 <template><div class="tinymce-container" ref="tinymceContainer"><Eidtor api-key="" v-if…

一路串联电机的绕制原理

这里要说明的一点是 对于一路串联的电机&#xff0c;无论是一把线圈还是两把线圈&#xff0c;出来的都是只有两个线头&#xff0c;可看做一个整体来对待&#xff01; 绕制具体原理 同心式线圈绕制 前面说的都是等距式的 线圈绕制&#xff0c;下面我们讲解一下同心式的绕制办法…

numpy学习笔记8

对不同列标识不同信息进行数据读取时 步骤&#xff1a; 1.根据不同列的数据标识的含义和类型&#xff0c;因此需要自定义数据类型 2.使用自定义的数据类型读取数据 有点类似于数据库读取数据操作 例子: # 1. 以上数据由于不同列数据标识的含义和类型不同,因此需要自定义数据类…

整体意义的构成与构建

输入数据中不同部分之间的复杂关系是指数据中各个元素如何相互作用和相互影响以构成整体意义的能力。 理解上述观点可以从以下几个方面进行&#xff1a; 元素的相互作用&#xff1a;在任何给定的数据集中&#xff0c;单个数据元素往往不是孤立存在的。它们之间存在某种形式的交…

692. 前K个高频单词 、KY264 单词识别

692. 前K个高频单词 - 力扣&#xff08;LeetCode&#xff09; //#include<set> class Solution { public://仿函数struct compare{bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2){return kv1.second > kv2.second;}…

天星金融(原小米金融)持续稳健发展,小米集团2023年质量效益实现双提升

近期&#xff0c;小米集团发布了其2023年财报。数据显示&#xff0c;小米集团在过去的一年里取得了令人瞩目的成绩&#xff0c;总收入达到了2710亿元&#xff0c;经调整后的净利润高达193亿元&#xff0c;同比大增126.3%。 2023年是小米贯彻执行“规模与利润并重”经营策略的第…

##07 从线性回归到神经网络:PyTorch实战解析

文章目录 前言线性回归基础理论背景实现步骤 神经网络介绍理论背景实现步骤 从线性回归到神经网络结论 前言 在深度学习的浩瀚宇宙中&#xff0c;线性回归和神经网络是两个基本但极其重要的概念。线性回归模型是统计学和机器学习的基石之一&#xff0c;而神经网络则是深度学习…

大模型能提高工作效率吗?

大模型可以通过多种方式提高办公效率&#xff0c;尤其是在处理信息和数据方面。 下是一些具体的应用场景&#xff1a; 自动化文档处理&#xff1a; 大模型可以自动处理文档&#xff0c;包括阅读、理解和总结长篇文章或报告&#xff0c;以及自动生成摘要或关键点。 自然语言生…

[开发|鸿蒙] DevEco Studio编译构建(笔记,持续更新)

构建体系 编译构建是将应用/服务的源代码、资源、第三方库等&#xff0c;通过编译工具转换为可直接在硬件设备上运行的二进制机器码&#xff0c;然后再将二进制机器码封装为HAP/APP软件包&#xff0c;并为HAP/APP包进行签名的过程。其中&#xff0c;HAP是可以直接运行在模拟器…

C++算法题 - 二叉树(2)

TOC 114. 二叉树展开为链表 LeetCode_link 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与…

2024年全国五大数学建模竞赛Top榜及难度分析!推荐数维杯!!!

发现最近许多同学都陆续开始准备今年的数学建模竞赛了&#xff0c;但是随着数学建模领域越来越普及&#xff0c;影响力越来越广泛&#xff0c;参加的同学也越来越多&#xff0c;就导致有越来越多各式各样的数学建模竞赛此起彼伏出现&#xff0c;但其中有一些竞赛其实并不值得参…

嵌入式学习day13

每日面试题 解释堆和栈的区别 ①申请方式 stack&#xff08;栈&#xff09;&#xff1a;由编译器自带分配释放&#xff0c;存在函数的参数值&#xff0c;局部变量等。 heap&#xff08;堆&#xff09;&#xff1a;程序员自己申请&#xff0c;并指明大小&#xff08;malloc函数…

如何用Jmeter压测

推荐你阅读 互联网大厂万字专题总结 Redis总结 JUC总结 操作系统总结 JVM总结 Mysql总结 微服务总结 互联网大厂常考知识点 什么是系统调用 CPU底层锁指令有哪些 AQS与ReentrantLock原理 旁路策略缓存一致性 Java通配符看这一篇就够 Java自限定泛型 技术分享 如何vscode中刷力扣…

考研管理类联考(专业代码199)数学基础【3】函数、方程、不等式

一、函数 1.一次函数 y kx b(k≠0) 的图象及性质 2.二次函数y ax^2 bx c的图象和性质 3.指数函数y a^x &#xff08; a&#xff1e;0&#xff0c;且a≠1&#xff09;的图象和性质 4.对数函数y logₐx ( a&#xff1e;0&#xff0c;且a≠1)的图象与性质 二、方程 1.一元…

Redis 实战之创建并修改 Lua 环境

创建并修改 Lua 环境 创建 Lua 环境载入函数库创建 redis 全局表格使用 Redis 自制的随机函数来替换 Lua 原有的随机函数创建排序辅助函数创建 redis.pcall 函数的错误报告辅助函数保护 Lua 的全局环境将 Lua 环境保存到服务器状态的 lua 属性里面总结 为了在Redis 服务器中执行…

Git系列:git merge 使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

产品评测:SmartX 与 Nutanix 超融合在数据库场景下的性能表现

重点内容 SmartX 与 Nutanix 超融合分布式存储设计差异如何影响数据库性能表现。重点测试结论&#xff1a;数据库场景下&#xff0c;SmartX 超融合基于单卷部署的性能&#xff0c;依旧优于 Nutanix 超融合基于多卷部署最佳配置的性能。更多 SmartX、VMware、Nutanix 超融合技术…

50kw 直流充电桩测试仪的基础知识

直流充电桩测试仪是专门用于检测和测试直流充电桩性能的设备。它能够对充电桩的输出电压、电流、功率、效率等关键参数进行精确测量&#xff0c;以确保充电桩的正常运行和充电安全。 一、工作原理 直流充电桩测试仪主要通过模拟实际充电过程&#xff0c;对充电桩的各项性能进行…