springmvc 源碼分析(三) -- 自定義處理器映射器和自定義處理器適配器,以及自定義參數解析器 和錯誤跳轉自定頁面


 測試環境搭建: 本次搭建是基於springboot來實現的,代碼在碼雲的鏈接:https://gitee.com/yangxioahui/thymeleaf.git

  DispatcherServlet核心流程在上一篇源碼分析已經做了詳細講解 了,參考: https://www.cnblogs.com/yangxiaohui227/p/13229413.html 

1. 自定義處理器映射器和處理器適配器:

本次目標是使得我自定義的controller生效

 

 

 

 

 //自定義映射器,使得springmvc 可以通過/test22 找到我的MyTestController

@Component
public class MyHandlerMapping implements HandlerMapping, ApplicationContextAware, Ordered {
    private ApplicationContext  applicationContext;

    private Map<String,Object> handlerMap=new HashMap<>();  //spring 在啟動時,會將所有被我的@MyRequestMapping注解標注的bean 存到這里,例如我們定義的MyTestController

    @Override
    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        String requestURI = request.getRequestURI(); //從請求參數中拿到對應的url
        Object o = handlerMap.get(requestURI); //通過url或者handler
        if(null!=o){
            HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(o);
            return handlerExecutionChain;
        }
        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
        this.detectHandler(applicationContext); //spring 容器創建該bean時會回調該方法

    }

    private void detectHandler(ApplicationContext applicationContext) {
        //獲取所有的BeanName
        String[] definitionNames = applicationContext.getBeanDefinitionNames(); 
        for (String beanName : definitionNames) {
            //獲取對應的bean
            Object bean = applicationContext.getBean(beanName);
            //判斷是否貼有我們要的注解
            MyRequestMapping annotation = bean.getClass().getAnnotation(MyRequestMapping.class);
            if(null!=annotation){
                String url = annotation.url();
                handlerMap.put(url,bean);
            }
        }
    }

    @Override
    public int getOrder() {  //因為springmvc 中有很多HandlerMapping,只要其中一個匹配到對應的url,就會返回,所以我們要將這里的優先級設置最高
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

 

 自定義適配器:

@Component
public class MyHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof ModlerInterface; //我們定義的適配器,處理的類型就是ModlerInterface
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ModlerInterface modlerInterface=(ModlerInterface)handler;
         modlerInterface.doHandler(request, response); //直接調用目標方法
        return null;
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return 0;
    }
}

 

 瀏覽器調用:

 

 原理分析:

 自定義映射器和自定義適配器添加到DispatcherServlet原理:

DispatcherServlet 的List<HandlerMapping> handlerMappings 初始化方法:

 

 DispatcherServlet的List<HandlerAdapter> handlerAdapters 的原理同上;

我們再debug調試下自定義HandlerMapping的過程: 

啟動項目:

 

 

 

 

 瀏覽器debug 調用我們的目標方法:

 

 

 

 

 

 

 

 

 

 

 

 這樣就找到我們的適配器了,之后通過適配器調用目標方法:

 

 

 

 

 至此,我們自定義映射器和適配器原理完成了;

2. 自定義參數解析器: 針對的是普通使用@RequestMaping實現的controller

 

 

 

 

 // 自定義參數解析器:

package com.yang.xiao.hui.thymeleaf.resovler;


import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDateResovler implements HandlerMethodArgumentResolver{

    @Override
    public boolean supportsParameter(MethodParameter parameter) { //@RequsetMapping注解標注的方法參數有沒指定注解
        StringToDate annotation = parameter.getParameter().getAnnotation(StringToDate.class);
        if(null!=annotation){
            return true;
        }
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String parameterName = parameter.getParameterName(); //獲取參數名稱
        HttpServletRequest  request = (HttpServletRequest)webRequest.getNativeRequest(); //獲取請求對象
        String value = request.getParameter(parameterName); //請求對象獲取參數值
        StringToDate annotation = parameter.getParameterAnnotation(StringToDate.class); //獲取參數的注解
        String format = annotation.value(); //獲取注解的值
        SimpleDateFormat dateFormat = new SimpleDateFormat(format); //格式化器
        Date date = dateFormat.parse(value);;//解析參數值為date
        return date;
    }
}

//自定定義返回值解析器

public class MyReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) { //判斷@RequestMapping注解標注的方法有沒有我們定義的注解
        MyResponseBody annotation = returnType.getMethod().getAnnotation(MyResponseBody.class);
        if(null!=annotation){
            return true;
        }
        return false;
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        mavContainer.setRequestHandled(true);//不會進行視圖解析了
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.getWriter().write(returnValue.toString());
    }
}

 

//根據之前的源碼分析知道,@RequestMapping 注解實現controller的方式,最終是由RequestMappingHandlerAdapter處理的,因此我們要將自定義的解析器加到RequestMappingHandlerAdapter中去;

 

 

 而RequestMappingHandlerAdapter這個bean的創建是通過WebMvcConfigurationSupport這個配置類來創建的,我們看看它的創建源碼

 

 

 

 

 所以我們分析下: getArgumentResolvers()方法

 

 

 

 到此,我們發現,自定義的參數解析器和返回值解析器生效的話,要繼承WebMvcConfigurationSupport這個類,並重寫相應方法:

@Configuration //重寫父類的方法可以實現很多功能
public class MyWebMvcConfiguration extends WebMvcConfigurationSupport {
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new StringToDateResovler()); //添加我們自定義的參數解析器
    }

    @Override
    protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        returnValueHandlers.add(new MyReturnValueHandler());//添加我們自定義的返回值解析器
    }
}

 

啟動代碼測試: 瀏覽器輸入: http://localhost:8081/find/product/3?date=2020-10-12

debug 調試,中間省略n步驟:下圖在(InvocableHandlerMethod類中)

 

 

 下圖是我們自定義的參數解析器


 

 

 

 

 

 

 

 瀏覽器拿到了結果:

 

 至此,我們自定義參數解析器和參數返回值解析器完成,springmvc很多自帶的解析器用於解析一些常用注解,如@ResponseBody,@ModlerAttribute@RequestBody等,他們的原理跟我們自定義的是一樣的

 

 3. 自定義異常處理器: 我們希望指定的異常,跳到指定的頁面 : 需求,我們希望NumberFormatException異常,跳到500.html這個頁面:

 

 

 

 我們自定義異常解析器,跟映射器和適配器一樣,只要注入spring容器即可,DispacherServlet中有個異常解析器集合:

 

 下面我們自定義異常解析器:

 

 我們定義一個測試Controller:

 

 //瀏覽器調用該方法:http://localhost:8081/test/error

 

 

 

 






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM