当前位置:   article > 正文

spring高级篇(七)

spring高级篇(七)

1、异常处理

        在DispatcherServlet中,doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理:

        在捕获到异常后没有立刻进行处理,而是先用一个局部变量dispatchException进行记录,然后统一由processDispatchResult() 方法进行处理:

        processDispatchResult() 方法中,首先判断异常是否为空,如果为空就不进行处理,然后判断是否是ModelAndViewDefiningException类型异常,如果不是就进入processHandlerException()

        processHandlerException() 中,会循环遍历handlerExceptionResolvers集合去匹配并处理异常:

  1. @Nullable
  2. private List<HandlerExceptionResolver> handlerExceptionResolvers;

        HandlerExceptionResolver是一个接口,我们使用ExceptionHandlerExceptionResolver的实现去模拟异常处理的过程:

         ExceptionHandlerExceptionResolver专门用于解析 @ExceptionHandler注解,把标注了 @ExceptionHandler注解的方法作为处理异常的方法

  1. //专门用于解析 @ExceptionHandler注解,把标注了 @ExceptionHandler注解的方法作为处理异常的方法
  2. ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
  3. //设置消息转换json
  4. resolver.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
  5. //初始化 加入常用的参数和返回值解析器
  6. resolver.afterPropertiesSet();
  7. testJSON(resolver);

        在afterPropertiesSet() 初始化方法中,已经预先定义好了一些参数解析器和返回值处理器:

        定义一个控制器:

  1. public class Controller1 {
  2. public void foo(){
  3. }
  4. /**
  5. * 处理异常的方法,并且将返回值转成JSON
  6. * @param e
  7. * @return
  8. */
  9. @ExceptionHandler
  10. @ResponseBody
  11. public Map<String,Object> handle(ArithmeticException e){
  12. return Collections.singletonMap("error",e.getMessage());
  13. }
  14. }

         resolver.resolveException()方法会检查Controller1中是否有@ExceptionHandler注解标注的方法,如果有,并且参数的异常和实际发生的异常能对应上,就执行其中的逻辑:

  1. private static void testJSON(ExceptionHandlerExceptionResolver resolver) throws NoSuchMethodException {
  2. MockHttpServletRequest request = new MockHttpServletRequest();
  3. MockHttpServletResponse response = new MockHttpServletResponse();
  4. //将控制器的foo方法封装成HandlerMethod对象
  5. HandlerMethod handlerMethod = new HandlerMethod(new Controller1(),Controller1.class.getMethod("foo"));
  6. //检查Controller1中是否有@ExceptionHandler注解标注的方法,如果有,并且参数的异常和实际发生的异常能对应上,就执行其中的逻辑
  7. resolver.resolveException(request,response,handlerMethod,new ArithmeticException("数学异常"));
  8. System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
  9. }

        此外处理异常的方法还支持ModelAndView类型的返回,与上述解析异常的过程相似。


        我们还可以转发自定义的错误处理页面:

  1. /**
  2. * 转发到自定义的错误页面
  3. * @return
  4. */
  5. @Bean
  6. public ErrorPageRegistrar errorPageRegistrar(){
  7. return new ErrorPageRegistrar() {
  8. @Override
  9. public void registerErrorPages(ErrorPageRegistry registry) {
  10. registry.addErrorPages(new ErrorPage("/error"));
  11. }
  12. };
  13. }
  14. /**
  15. * 注册后处理器
  16. * @return
  17. */
  18. @Bean
  19. public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor(){
  20. return new ErrorPageRegistrarBeanPostProcessor();
  21. }
  22. /**
  23. * Spring Boot中配置自定义的BasicErrorController,用于处理基本的错误页面和错误信息。
  24. * @return
  25. */
  26. @Bean
  27. public BasicErrorController basicErrorController(){
  28. ErrorProperties errorProperties = new ErrorProperties();
  29. errorProperties.setIncludeException(true);
  30. return new BasicErrorController(new DefaultErrorAttributes(),errorProperties);
  31. }

2、BeanNameUrlHandlerMapping&SimpleControllerHandlerAdapter

        BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter分别是HandlerMappingHandlerAdapter的实现类:

        在BeanNameUrlHandlerMapping中,以/开头的 bean 的名字会被当作映射路径。这些 bean 本身当作 handler,要求实现 Controller 接口。

        准备一个Config类,将BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter注册成bean:

  1. @Configuration
  2. @ComponentScan
  3. @PropertySource("classpath:application.properties")
  4. @EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class})//将配置文件中的属性绑定到对象中
  5. public class Config {
  6. /**
  7. * 注册内嵌web容器工厂 tomcat容器
  8. */
  9. @Bean
  10. public TomcatServletWebServerFactory tomcatServletWebServerFactory(){
  11. return new TomcatServletWebServerFactory();
  12. }
  13. /**
  14. * 创建DispatcherServlet
  15. * 首次使用时,由tomcat容器初始化
  16. * @return
  17. */
  18. @Bean
  19. public DispatcherServlet dispatcherServlet(){
  20. return new DispatcherServlet();
  21. }
  22. /**
  23. * 注册DispatcherServlet springmvc入口
  24. * @param dispatcherServlet
  25. * @return
  26. */
  27. @Bean
  28. public DispatcherServletRegistrationBean dispatcherServletRegistrationBean
  29. (DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties){
  30. DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
  31. //设置tomcat容器启动时即进行DispatcherServlet初始化
  32. registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
  33. return registrationBean;
  34. }
  35. @Bean
  36. public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping(){
  37. return new BeanNameUrlHandlerMapping();
  38. }
  39. @Bean
  40. public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter(){
  41. return new SimpleControllerHandlerAdapter();
  42. }
  43. @Bean("/c3")
  44. public Controller controller3(){
  45. return (request, response) -> {
  46. response.getWriter().print("this is c3");
  47. return null;
  48. };
  49. }
  50. }

        再准备两个实现了Controller接口的控制器类:

  1. @Component("/c1")
  2. public class Controller1 implements Controller {
  3. @Override
  4. public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
  5. response.getWriter().print("this is c1");
  6. return null;
  7. }
  8. }
  1. @Component("c2")
  2. public class Controller2 implements Controller {
  3. @Override
  4. public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
  5. response.getWriter().print("this is c2");
  6. return null;
  7. }
  8. }

        启动主类:

  1. public class A31 {
  2. public static void main(String[] args) {
  3. AnnotationConfigServletWebServerApplicationContext context =
  4. new AnnotationConfigServletWebServerApplicationContext(Config.class);
  5. }
  6. }


        我们可以模拟实现这一组映射器和适配器:

        定义一个类实现BeanNameUrlHandlerMapping的顶级接口HandlerMapping:

        它的作用是在初始化时收集容器中所有以/开头的路径和类成map集合,并且在调用时会判断当前requestURI能否与map集合中的任意元素相匹配:

        (复习一下,容器初始化时会收集所有 @RequestMapping 映射信息,封装为 Map)

  1. /**
  2. * 模拟处理器映射器
  3. * 收集请求中以/开头的bean
  4. */
  5. @Component
  6. public class MyHandlerMapping implements HandlerMapping {
  7. /**
  8. * 处理器映射器,getHandlerMethods中 和当前requestURI 匹配的路径信息
  9. * @param request
  10. * @return
  11. * @throws Exception
  12. */
  13. @Override
  14. public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  15. String requestURI = request.getRequestURI();
  16. Controller controller = map.get(requestURI);
  17. //匹配不上 404
  18. if (controller == null){
  19. return null;
  20. }
  21. return new HandlerExecutionChain(controller);
  22. }
  23. @Autowired
  24. private ApplicationContext applicationContext;
  25. private Map<String, Controller> map;
  26. /**
  27. * 初始化时收集所有容器中/开头的bean信息
  28. */
  29. @PostConstruct
  30. public void init() {
  31. Map<String, Controller> beansOfType = applicationContext.getBeansOfType(Controller.class);
  32. map = beansOfType.entrySet().stream().filter(e -> e.getKey().startsWith("/")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  33. System.out.println(map);
  34. }
  35. }

        定义一个类实现HandlerAdapter作为适配器,负责将请求分派给实现了controller接口类中的方法,RequestMappingHandlerAdapter相比,不需要自定义参数和返回值处理器。

  1. /**
  2. * 模拟处理器适配器
  3. */
  4. @Component
  5. public class MyHandlerAdapter implements HandlerAdapter {
  6. /**
  7. * 判断传递的handler是否是当前MyHandlerAdapt支持的
  8. * @param handler
  9. * @return
  10. */
  11. @Override
  12. public boolean supports(Object handler) {
  13. return handler instanceof Controller;
  14. }
  15. /**
  16. * 调用实现了Controller接口的方法
  17. * 无需参数解析器,返回值处理器
  18. * @param request
  19. * @param response
  20. * @param handler
  21. * @return
  22. * @throws Exception
  23. */
  24. @Override
  25. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  26. if (handler instanceof Controller){
  27. ((Controller) handler).handleRequest(request, response);
  28. }
  29. return null;
  30. }
  31. @Override
  32. public long getLastModified(HttpServletRequest request, Object handler) {
  33. return -1;
  34. }
  35. }

        结论:Controller1和Controller3能匹配上,Controller2匹配不上(路径中没有/)

3、RouterFunctionMapping&HandlerFunctionAdapter

        RouterFunctionMapping和HandlerFunctionAdapter也是HandlerMappingHandlerAdapter的实现类:

  • RouterFunctionMapping会收集所有的RouterFunction,请求到达时,根据条件找到HandlerFunction
  • HandlerFunctionAdapter会调用符合条件的HandlerFunction。
  1. /**
  2. * 会收集所有的RouterFunction
  3. * 请求到达时,根据条件找到HandlerFunction
  4. * @return
  5. */
  6. @Bean
  7. public RouterFunctionMapping routerFunctionMapping(){
  8. return new RouterFunctionMapping();
  9. }
  10. /**
  11. * 调用符合条件的HandlerFunction
  12. * @return
  13. */
  14. @Bean
  15. public HandlerFunctionAdapter handlerFunctionAdapter(){
  16. return new HandlerFunctionAdapter();
  17. }

        RouterFunction分为两部分:匹配规则和具体的执行逻辑(请求是GET类型,并且路径是/r1,就执行new HandlerFunction<ServerResponse>()中的逻辑)

  1. @Bean
  2. public RouterFunction<ServerResponse> r1(){
  3. //参数一 匹配规则 参数二 具体的执行逻辑
  4. return RouterFunctions.route(RequestPredicates.GET("/r1"), new HandlerFunction<ServerResponse>() {
  5. @Override
  6. public ServerResponse handle(ServerRequest request) throws Exception {
  7. return ServerResponse.ok().body("this is r1");
  8. }
  9. });
  10. }

下一篇对Spring MVC 的执行流程做一个总结。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/533422
推荐阅读
相关标签
  

闽ICP备14008679号