spring高级篇(七)

embedded/2024/10/18 14:23:44/

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.ppmy.cn/embedded/38173.html

相关文章

python基础---函数以及常用变量

函数及变量 函数 使用def关键字 在参数名前面的*表示args是一个可变参数 def add(*args):total 0for val in args:total valreturn total# 在调用add函数时可以传入0个或多个参数 print(add()) print(add(1)) print(add(1, 2)) print(add(1, 2, 3)) print(add(1, 3, 5, 7…

2-qt之信号与槽-简单实例讲解

前言、因实践课程讲解需求&#xff0c;简单介绍下qt的信号与槽。 一、了解信号与槽 怎样使用信号与槽&#xff1f; 概览 还记得 X-Window 上老旧的回调函数系统吗&#xff1f;通常它不是类型安全的并且很复杂。&#xff08;使用&#xff09;它&#xff08;会&#xff09;有很多…

软考143-下午题-【试题二】:E-R图、关系模式

一、分值与目标 15分&#xff0c;目标10 二、题目形式 示例&#xff1a; 三、E-R图的基本图形元素 示例&#xff1a; 3-1、实体 1、弱实体 在现实世界中有一种特殊的联系&#xff0c;这种联系代表实体间的所有 (Ownership) 关系&#xff0c;例如&#xff1a;职工与家属的联系…

HTML5实现酷炫个人产品推广、工具推广、信息推广、个人主页、个人介绍、酷炫官网、门户网站模板源码

文章目录 1.设计来源1.1 主界面1.2 我的产品界面1.3 关于我们界面1.4 照片墙界面1.5 发展历程界面1.6 优秀人才界面1.7 热门产品界面1.8 联系我们界面 2.灵活调整模块3.效果和源码3.1 动态效果3.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.c…

C语言----杨辉三角

各位看官们好。学习到这里想必大家应该对C语言的了解也是很深刻的了吧。但是我们也不能忘记我们一起学习的知识啊。在我们以前学习C语言的时候我想大家应该都听说过杨辉三角吧。虽然我们把其中的规律找到那么这个代码就简单很多了。那么接下里我们就来讲讲杨辉三角。 首先我们先…

【项目学习01_2024.05.01_Day03】

学习笔记 3.6 开发业务层3.6.1 创建数据字典表3.6.2 编写Service3.6.3 测试Service 3.7 接口测试3.7.1 接口完善3.7.2 Httpclient测试 3.8 前后端联调3.8.1 准备环境3.8.2 安装系统管理服务3.8.3 解决跨域问题解决跨域的方法&#xff1a;我们准备使用方案2解决跨域问题。在内容…

QX-mini51单片机学习-----(3)流水灯

目录 1宏定义 2函数的定义 3延时函数 4标准库函数中的循环移位函数 5循环移位函数与左移和右移运算符的区别 6实例 7keil中DeBug的用法 1宏定义 是预处理语句不需要分号 #define uchar unsigned char//此时uchar代替unsigned char typedef是关键字 后面是接分号…

如何购买阿里云99计划的ECS云服务器?99元购买阿里云2核2G3M服务器教程

阿里云助力中小企业和开发者无忧上云的“99计划”中有两款性价比超高的ECS云服务器&#xff0c;2026年3月31日活动结束前新购和续费价格一样。 其中个人和企业新老用户同享的2核2G3M服务器仅需99元/年&#xff08;续费同价&#xff09;&#xff0c;企业新老用户同学的2核4G5M仅…