測試環境搭建: 本次搭建是基於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