网页制作与网站建设答案,个人网站主机的配置,99设计网站,建设银行高校缴费网站目录 简介
调用流程
1. 首先#xff0c;还是需要进行到前端控制器的doDispatch方法#xff0c;这是我们的调用Spring MVC的核心入口方法
2. 在doDispatch方法内部#xff0c;我们调用到了HandlerAdapter.handle(*****) 方法
3. 最终#xff0c;我们会来到 RequestMappi…目录 简介
调用流程
1. 首先还是需要进行到前端控制器的doDispatch方法这是我们的调用Spring MVC的核心入口方法
2. 在doDispatch方法内部我们调用到了HandlerAdapter.handle(*****) 方法
3. 最终我们会来到 RequestMappingHandlerAdapter 的 invokeHandlerMethod 方法在这个方法内部我们会设置 参数解析器 和 返回值解析器
4. 来到当前方法 invokeHandlerMethod 调用 Controller业务类处进行业务类的调用进行 参数解析 与 返回值处理 5. 进入 ServletInvocableHandlerMethod 的 invokeAndHandle 方法处进行参数解析 与 返回值处理
参数解析
case1RequestParam注解的参数解析器
case 2 PathVariable 注解的参数解析器 简介
据我所知
参数解析器共分26种针对不同的注解参数有不同的参数解析器
返回值解析器共分15种针对不同的返回值调用不同的返回值解析器
其实参数解析的核心代码在 HandlerMethodArgumentResolverComposite类resolveArgument 方法处以后直接锁定这个方法就可以快速进行debug操作 上一篇博客Spring MVC 的调用12_chen_yao_kerr的博客-CSDN博客已经对Spring MVC整体调用流程进行了梳理但是在上一篇中关于HandlerAdapter调用到业务类最终返回modelAndView的步骤中我们只是梳理了整体流程, 并没有详细的分析如何进行参数传递的。本篇则是对上一篇的第五、六、七步中进行参数解析的补充.
其实参数解析就是发生在HandlerAdapter调用到具体的业务类之前进行的看下图 调用流程
1. 首先还是需要进行到前端控制器的doDispatch方法这是我们的调用Spring MVC的核心入口方法
2. 在doDispatch方法内部我们调用到了HandlerAdapter.handle(*****) 方法
3. 最终我们会来到 RequestMappingHandlerAdapter 的 invokeHandlerMethod 方法在这个方法内部我们会设置 参数解析器 和 返回值解析器
参数解析器有26种 返回值解析器 源码如下
Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {//获取数据绑定工厂 InitBinder注解支持没太多用WebDataBinderFactory binderFactory getDataBinderFactory(handlerMethod);//Model工厂,收集了ModelAttribute注解的方法ModelFactory modelFactory getModelFactory(handlerMethod, binderFactory);//可调用的方法对象ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {//设置参数解析器invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers ! null) {//设置返回值解析器invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}//设置参数绑定工厂invocableMethod.setDataBinderFactory(binderFactory);//设置参数名称解析类invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);ModelAndViewContainer mavContainer new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));//调用有ModelAttribute注解的方法。每次请求都会调用有ModelAttribute注解的方法//把ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的map中了modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);//异步处理WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result asyncManager.getConcurrentResult();mavContainer (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn - {String formatted LogFormatUtils.formatValue(result, !traceOn);return Resume with async result [ formatted ];});invocableMethod invocableMethod.wrapConcurrentResult(result);}//Controller方法调用重点看看invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}} 4. 来到当前方法 invokeHandlerMethod 调用 Controller业务类处进行业务类的调用进行 参数解析 与 返回值处理 5. 进入 ServletInvocableHandlerMethod 的 invokeAndHandle 方法处进行参数解析 与 返回值处理 参数解析
参数解析需要针对不同的注解传递的参数进行解析不能一概而论首先来到参数解析的方法中。即 ServletInvocableHandlerMethod 类的 invokeForRequest 方法中 public Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//获取参数数组,重点看Object[] args getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace(Arguments: Arrays.toString(args));}return doInvoke(args);}
就是获取到所有的参数进行反射调用反射调用在分析Spring源码的时候已经说过很多次了就不再累赘了。下面针对不同的case分析不同的参数解析流程 case1RequestParam注解的参数解析器
测试的业务方法 RequestMapping(/queryUser)public String queryUser(RequestParam String language, HttpSession session) {session.setAttribute(language,language);return ok;} URL http://localhost:9090/user/queryUser?languageen 测试结果
源码分析
1、首先肯定是来到参数解析的核心方法处 2. 进入核心方法 a, 首先拿到当前方法对应的参数名称、参数类型、参数使用的注解、当前方法属于的哪个类、参数解析器等各种各样信息; b. 进入参数解析的核心方法 c. 根据参数获取对应的参数解析器 其实就是根据步骤 a 获取到的 MethodParameter 对象直接从缓存中拿到对应的参数解析器而已。拿到的参数解析器是 RequestParamMethodArgumentResolver。 d. 最后自然是调用这个类进行参数解析了。具体调用的是 resolveArgument 方法进行 参数解析并返回数组了。 其实参数解析很简单就是获取到参数的包装类根据包装类获取到当前参数名、根据参数名到Request中获取参数值此处就是调用 request.getParameterValues(name) 最终返回获取到的实际参数值并且返回。因为每个方法的参数可能是多个因此我们是按照方法的参数进行遍历操作的而且每个参数都是有对应下标的这样返回值就不会乱、也不会少 。 case 2 PathVariable 注解的参数解析器
测试业务方法 RequestMapping(/queryUser2/{id})public ResponseBody String queryUser2(PathVariable(id) String userId) {return hello user : userId;}
URLhttp://localhost:9090/user/queryUser2/1234
测试结果 源码分析
文章开头就说了 参数解析的核心代码在HandlerMethodArgumentResolverComposite类resolveArgument 方法处直接把断点打在此处
获取参数解析器PathVariable 对应的参数解析器为 PathVariableMethodArgumentResolver 根据参数解析器进行参数解析 我们发现这是相同的代码进去以后其实会调用父类的钩子方法返回到当前参数解析器 PathVariableMethodArgumentResolver中调用钩子方法中 其实它就是根据拿 requet.getAttribute(***)获取参数值而已。所谓的Spring MVC其实就是对 Servlet调用的封装而已。传统的 jsp servlet调用我们都是通过 request 直接拿参数的。而Spring mvc 自己封装了 request拿参数的过程而已。 下面列出常用的参数解析器以及对应的注解我们直接看解析器的 resolveName 方法就可以知道具体的解析过程。
RequestHeaderMethodArgumentResolver 用来处理使用了 RequestHeader 注解并且参数类型不是 Map 的参数RequestHeaderMapMethodArgumentResolver 很明显这个用来处理使用 RequestHeader 注解并且参数类型是 Map 的参数PathVariableMethodArgumentResolver 处理使用 PathVariable 注解并且参数类型不为 Map 的参数PathVariableMapMethodArgumentResolver 处理使用 PathVariable 注解并且参数类型为 Map 的参数RequestAttributeMethodArgumentResolver 处理使用 RequestAttribute 注解的参数ServletCookieValueMethodArgumentResolver 处理使用 CookieValue 的参数SessionAttributeMethodArgumentResolver 处理使用 SessionAttribute 注解的参数RequestParamMethodArgumentResolver 这个功能就比较广了。使用了 RequestParam 注解的参数、文件上传的类型 MultipartFile、或者一些没有使用任何注解的基本类型Long、Integer以及 String 等都使用该参数解析器处理。变量不能是MapRequestParamMapMethodArgumentResolver 针对上一个提供Map支持 至于不常用的参数解析器按照我说的方法直接找到以下方法打断点就可以快速锁定