赞
踩
在DispatcherServlet中,doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理:
在捕获到异常后没有立刻进行处理,而是先用一个局部变量dispatchException进行记录,然后统一由processDispatchResult() 方法进行处理:
processDispatchResult() 方法中,首先判断异常是否为空,如果为空就不进行处理,然后判断是否是ModelAndViewDefiningException类型异常,如果不是就进入processHandlerException()
processHandlerException() 中,会循环遍历handlerExceptionResolvers集合去匹配并处理异常:
- @Nullable
- private 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
- @ResponseBody
- public 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
- */
- @Bean
- public ErrorPageRegistrar errorPageRegistrar(){
- return new ErrorPageRegistrar() {
- @Override
- public void registerErrorPages(ErrorPageRegistry registry) {
- registry.addErrorPages(new ErrorPage("/error"));
- }
- };
- }
-
- /**
- * 注册后处理器
- * @return
- */
- @Bean
- public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor(){
- return new ErrorPageRegistrarBeanPostProcessor();
- }
-
-
- /**
- * Spring Boot中配置自定义的BasicErrorController,用于处理基本的错误页面和错误信息。
- * @return
- */
- @Bean
- public BasicErrorController basicErrorController(){
- ErrorProperties errorProperties = new ErrorProperties();
- errorProperties.setIncludeException(true);
- return new BasicErrorController(new DefaultErrorAttributes(),errorProperties);
- }
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容器
- */
- @Bean
- public TomcatServletWebServerFactory tomcatServletWebServerFactory(){
- return new TomcatServletWebServerFactory();
- }
-
- /**
- * 创建DispatcherServlet
- * 首次使用时,由tomcat容器初始化
- * @return
- */
- @Bean
- public DispatcherServlet dispatcherServlet(){
- return new DispatcherServlet();
- }
-
- /**
- * 注册DispatcherServlet springmvc入口
- * @param dispatcherServlet
- * @return
- */
- @Bean
- public DispatcherServletRegistrationBean dispatcherServletRegistrationBean
- (DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties){
- DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
- //设置tomcat容器启动时即进行DispatcherServlet初始化
- registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
- return registrationBean;
- }
-
- @Bean
- public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping(){
- return new BeanNameUrlHandlerMapping();
- }
-
- @Bean
- public 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 {
- @Override
- public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
- response.getWriter().print("this is c1");
- return null;
- }
- }
- @Component("c2")
- public class Controller2 implements Controller {
-
- @Override
- public 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
- */
- @Override
- public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- String requestURI = request.getRequestURI();
- Controller controller = map.get(requestURI);
- //匹配不上 404
- if (controller == null){
- return null;
- }
- return new HandlerExecutionChain(controller);
- }
-
-
-
-
- @Autowired
- private ApplicationContext applicationContext;
-
- private Map<String, Controller> map;
-
- /**
- * 初始化时收集所有容器中/开头的bean信息
- */
- @PostConstruct
- public 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
- */
- @Override
- public boolean supports(Object handler) {
- return handler instanceof Controller;
- }
-
- /**
- * 调用实现了Controller接口的方法
- * 无需参数解析器,返回值处理器
- * @param request
- * @param response
- * @param handler
- * @return
- * @throws Exception
- */
- @Override
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- if (handler instanceof Controller){
- ((Controller) handler).handleRequest(request, response);
- }
- return null;
- }
-
- @Override
- public long getLastModified(HttpServletRequest request, Object handler) {
- return -1;
- }
- }
结论:Controller1和Controller3能匹配上,Controller2匹配不上(路径中没有/)
RouterFunctionMapping和HandlerFunctionAdapter也是HandlerMapping和HandlerAdapter的实现类:
- /**
- * 会收集所有的RouterFunction
- * 请求到达时,根据条件找到HandlerFunction
- * @return
- */
- @Bean
- public RouterFunctionMapping routerFunctionMapping(){
- return new RouterFunctionMapping();
- }
-
- /**
- * 调用符合条件的HandlerFunction
- * @return
- */
- @Bean
- public HandlerFunctionAdapter handlerFunctionAdapter(){
- return new HandlerFunctionAdapter();
- }
RouterFunction分为两部分:匹配规则和具体的执行逻辑(请求是GET类型,并且路径是/r1,就执行new HandlerFunction<ServerResponse>()中的逻辑)
- @Bean
- public RouterFunction<ServerResponse> r1(){
- //参数一 匹配规则 参数二 具体的执行逻辑
- return RouterFunctions.route(RequestPredicates.GET("/r1"), new HandlerFunction<ServerResponse>() {
- @Override
- public ServerResponse handle(ServerRequest request) throws Exception {
- return ServerResponse.ok().body("this is r1");
- }
- });
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。