深圳建设网站上市,广东炒股配资网站开发,深圳网站建设南山,局域网搭建目录 SpringMVC流程
源码分析 第一步:用户发起请求到前端控制器#xff08;DispatcherServlet#xff09;
第二步#xff1a;前端控制器请求处理器映射器#xff08;HandlerMappering#xff09;去查找处理器#xff08;Handle#xff09;#xff1a;通过xml配置或者…目录 SpringMVC流程
源码分析 第一步:用户发起请求到前端控制器DispatcherServlet
第二步前端控制器请求处理器映射器HandlerMappering去查找处理器Handle通过xml配置或者注解进行查找
1. 根据请求的uri拿到对应的HandlerMethod对象
第三步找到以后处理器映射器HandlerMappering像前端控制器返回执行链HandlerExecutionChain
第四步前端控制器DispatcherServlet调用处理器适配器HandlerAdapter去执行处理器Handler
第五步处理器适配器去执行Handler
第六步Handler执行完给处理器适配器返回ModelAndView
第七步处理器适配器向前端控制器返回ModelAndView
ModelAndView演示
第八步前端控制器请求视图解析器ViewResolver去进行视图解析并返回View
第九步视图解析器像前端控制器返回View
第十步前端控制器对视图进行渲染
第十一步前端控制器向用户响应结果 SpringMVC流程 第一步:用户发起请求到前端控制器DispatcherServlet
第二步前端控制器请求处理器映射器HandlerMappering去查找处理器Handle通过xml配置或者注解进行查找
第三步找到以后处理器映射器HandlerMappering像前端控制器返回执行链HandlerExecutionChain
第四步前端控制器DispatcherServlet调用处理器适配器HandlerAdapter去执行处理器Handler
第五步处理器适配器去执行Handler
第六步Handler执行完给处理器适配器返回ModelAndView
第七步处理器适配器向前端控制器返回ModelAndView
第八步前端控制器请求视图解析器ViewResolver去进行视图解析
第九步视图解析器像前端控制器返回View
第十步前端控制器对视图进行渲染
第十一步前端控制器向用户响应结果 此处需要强调的是
DispatcherServlet作为前端控制器整个流程控制的中心控制其它组件执行统一调度降低组件之间的耦合性提高每个组件的扩展性。
HandlerMapping通过扩展处理器映射器实现不同的映射方式例如配置文件方式实现接口方式注解方式等。
HandlAdapter通过扩展处理器适配器支持更多类型的处理器。
ViewResolver通过扩展视图解析器支持更多类型的视图解析例如jsp、freemarker、pdf、excel等。 源码分析 第一步:用户发起请求到前端控制器DispatcherServlet
其实就是前端请求调用到了DispatcherServlet的doService方法而doService方法又调用到了doDispatch 核心方法 而 doDispatch 方法负责整个Spring MVC的调度与响应是核心中的核心 第二步前端控制器请求处理器映射器HandlerMappering去查找处理器Handle通过xml配置或者注解进行查找 其实就是在doDispatch内部调用了 getHandler 方法它会获取到所有的handlerMapping对象进行遍历找到符合匹配规则的handlerMapping就直接返回
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//handlerMappering实例if (this.handlerMappings ! null) {for (HandlerMapping mapping : this.handlerMappings) {//获取HandlerMethod和过滤器链的包装类HandlerExecutionChain handler mapping.getHandler(request);if (handler ! null) {return handler;}}}return null;}
注意 以上的for循环遍历所有的handlerMapping类并且调用每个类的都调用了getHandler方法是一个策略设计模式可以避免大量的 if 判断。
这里我们需要关注一下requestMappingHandlerMapping这个对象因为上一篇博客中我们提到了是在requestMappingHandlerMapping创建完以后建立映射关系的。遍历到requestMappingHandlerMapping对象后进入getHanlder方法 内部 1. 根据请求的uri拿到对应的HandlerMethod对象
其实就是调用 getHandlerInternal 方法的过程上一讲我们提到根据 URL 和 requestMappingInfo建立了映射而requestMappingInfo又和HandlerMethod建立了映射此处肯定也是按照这个规则逐步去查找HandlerMethod对象的。 下面进行源码验证: 进入这个方法内部
首先我们发现它根据URL 拿到了 requestMappingInfo 对象 其次根据requestMappingInfo拿HandlerMethod对象进入这个方法内部进行确认 第三步找到以后处理器映射器HandlerMappering像前端控制器返回执行链HandlerExecutionChain 主方法 进入这个方法内部看看具体是如何 获取 HandlerExecutionChain 对象的 补充说明一下拦截器是我们自己定义的它有3个方法分别问前置过滤器、中置过滤器 和 后置过滤器
package com.xiangxue.jack.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;Component
public class UserInterceptor implements HandlerInterceptor {//前置过滤器Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(UserInterceptor用户权限校验);return true;}//中置过滤器Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println(UserInterceptor修改modelAndView);}//后置过滤器Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println(UserInterceptor资源释放);}
}而拦截器是在 有 EnableWebMvc的类 并且这个类继承了 WebMvcConfigurerAdapter类 中添加进去的。 第四步前端控制器DispatcherServlet调用处理器适配器HandlerAdapter去执行处理器Handler
回到核心方法 doDispatch 中在这个方法内部调用了 getHandlerAdapter 进行获取处理器适配器的 进入这个方法内部 看看具体是如何获取到处理器适配器的 第五步处理器适配器去执行Handler
第六步Handler执行完给处理器适配器返回ModelAndView
第七步处理器适配器向前端控制器返回ModelAndView
第五、六、七步可以合起来一起分析。第三步的时候说到了拦截器其实在执行hand方法之前会进行 拦截器 中的前置过滤器的调用 成功进入到了我们自己写的拦截器类中并且调用到了前置过滤器方法 言归正传处理器去执行handler其实就是调用到我们的Controller类的对应方法而已。就是进入到业务类中进行实际的业务调用。最终返回的数据类型包装成ModelAndView返回给处理器适配器 而处理器适配器向前端控制器返回ModelAndView。 还需要补充一点中置过滤器的调用时序 是当 ha.handle 掉完以后 也就是 Controller 里面具体方法调用完以后才轮到中置过滤器调用。 而中置拦截器可以修改 modelAndView。 成功调到中置过滤器 ModelAndView就是最原始的Spring MVC返回值如果使用了ResponseBody注解或者其他注解改变返回值那就没有ModelAndView了。 ModelAndView演示
首先定义jsp 因为ModelAndView需要按照特定的规则去解析需要有对应的jsp文件 重写中置过滤器 设置MVC 的jsp路径 Controller 类 调用结果
URL http://localhost:9090/user/queryUser?languagetw
页面 URL http://localhost:9090/user/queryUser?languagezh
页面 第八步前端控制器请求视图解析器ViewResolver去进行视图解析并返回View
还是核心方法 doDispatch方法体中在执行完中置过滤器以后我们就开始进行视图渲染的方法调用了。 源码如下 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {boolean errorView false;//如果异常不为空if (exception ! null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug(ModelAndViewDefiningException encountered, exception);mv ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler (mappedHandler ! null ? mappedHandler.getHandler() : null);mv processHandlerException(request, response, handler, exception);errorView (mv ! null);}}// Did the handler return a view to render?//视图渲染响应视图if (mv ! null !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace(No view rendering, null ModelAndView returned.);}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}//后置过滤器if (mappedHandler ! null) {mappedHandler.triggerAfterCompletion(request, response, null);}}
核心方法就是 render(mv, request, response) 它负责视图的渲染与响应 获取到视图解析器 进入方法以后我们发现视图解析器会根据我们实际业务方法生成各种各样的View然后根据当前的方法获取到最优的View并进行返回直到返回到前端控制器DispatcherServlet中. 我们根据上图中的步骤逐步进行源码解析
1、创建视图View 调用 createView 方法创建新视图 View 最终会创建视图View并且对视图进行各种属性的设置比如视图的URL 2. 根据ContentType 以及 ViewName等很多的条件选取最佳的 View 而ContentType 则是定义在jsp页面中的 第九步视图解析器像前端控制器返回View
这一步最简单就是根据第八步选取的View一直进行返回直到DispatcherServlet中。 第十步前端控制器对视图进行渲染 核心方法
Overrideprotected void renderMergedOutputModel(MapString, Object model, HttpServletRequest request, HttpServletResponse response) throws Exception {//把响应数据设置到request对象中// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);//获取到跳转的地址// Determine the path for the request dispatcher.String dispatcherPath prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd getRequestDispatcher(request, dispatcherPath);if (rd null) {throw new ServletException(Could not get RequestDispatcher for [ getUrl() ]: Check that the corresponding file exists within your web application archive!);}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug(Including [ getUrl() ]);}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug(Forwarding to [ getUrl() ]);}rd.forward(request, response);}} 其实就是一个forward请求转发到对应的 jsp 页面而已。 第十一步前端控制器向用户响应结果
请求都转发到 jsp 页面了 这一步确实不知道该说些什么了。 就此结束本篇。 后置过滤器
后置过滤器是最后调用的目的就是释放一些资源总之就是最后干的事情。最终会调到我们自己定义的拦截器中的后置过滤器的方法中 自定义的后置拦截器中的后置过滤器方法 我们在使用Spring mvc的时候经常会使用各种各样的注解进行参数的传递比如 RequestParam 、PathVariable、ModelAttribute、CookieValue、RequestHeader等各种各样的注解。下一篇会专门针对这些注解挑出几个常用的进行分析。