前言
开心一刻
晚上陪老丈人吃饭, 突然手机响了, 我手贱按了免提...... 哥们: 快出来喝酒! 哥几个都在呢! 我: 今天不行, 我现在陪老丈人吃饭呢. 哥们: 那你抓紧喝, 我三杯白酒, 把我岳父放倒了才出来的, 你也快点. 看着我老丈人的脸, 我不知道该怎么回了......
猪一样的队友
遗留问题
在关于利用 maven 搭建 ssm 的博客, 我们一起来探讨下问的最多的问题中, 我遗留了一个问题: Spring mvc 是何时, 何地, 如何将 Model 中的属性绑定到哪个作用域, 这里的作用域指的是 Servlet 的四大作用域; 不了解问题背景的可以回过头去看看我的上篇博文.
明确的解答我会放到最后, 在解答问题之前, 我先和大家一起来捋一捋 Spring mvc 的工作原理. 废话不多说, 开始我们神秘的探险之旅!
应用示例
在讲工作原理之前, 我们先看一个简单的 spring mvc(ssm) 示例, 以及实现的效果
工程代码地址:
工程结构与效果如上所示, 我们不做过多的探究, 我们打起精神往下看本篇的重点
工作原理
准备 - 资源的加载与初始化
1,DispatcherServlet 静态初始化
DispatcherServlet 中有如下静态块
- static {
- // Load default strategy implementations from properties file.
- // This is currently strictly internal and not meant to be customized
- // by application developers.
- try {
- ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
- defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load'DispatcherServlet.properties':" + ex.getMessage());
- }
- }
这里会将 DispatcherServlet.properties 中的内容读取到 DispatcherServlet 的属性: private static final Properties defaultStrategies 中, DispatcherServlet.properties 内容如下
- # Default implementation classes for DispatcherServlet's strategy interfaces.
- # Used as fallback when no matching beans are found in the DispatcherServlet context.
- # Not meant to be customized by application developers.
- org.springframework.web.servlet.LocaleResolver=org.springframework.Web.servlet.I18N.AcceptHeaderLocaleResolver
- org.springframework.Web.servlet.ThemeResolver=org.springframework.Web.servlet.theme.FixedThemeResolver
- org.springframework.Web.servlet.HandlerMapping=org.springframework.Web.servlet.handler.BeanNameUrlHandlerMapping,\
- org.springframework.Web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
- org.springframework.Web.servlet.HandlerAdapter=org.springframework.Web.servlet.mvc.HttpRequestHandlerAdapter,\
- org.springframework.Web.servlet.mvc.SimpleControllerHandlerAdapter,\
- org.springframework.Web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
- org.springframework.Web.servlet.HandlerExceptionResolver=org.springframework.Web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
- org.springframework.Web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
- org.springframework.Web.servlet.mvc.support.DefaultHandlerExceptionResolver
- org.springframework.Web.servlet.RequestToViewNameTranslator=org.springframework.Web.servlet.view.DefaultRequestToViewNameTranslator
- org.springframework.Web.servlet.ViewResolver=org.springframework.Web.servlet.view.InternalResourceViewResolver
- org.springframework.Web.servlet.FlashMapManager=org.springframework.Web.servlet.support.SessionFlashMapManager
- View Code
指定了 DispatcherServlet 策略接口的默认实现, 后续 DispatcherServlet 初始化策略的时候会用到
2,interceptor 定义的加载
spring 启动过程中会调用 InterceptorsBeanDefinitionParser 的 parse 方法来解析出我们自定义的 interceptor 定义, 封装成 MappedInterceptor 类型的 bean 定义, 并放到 spring 容器中; 我们可以简单的认为 spring 容器中已经存在了我们自定义的 interceptor 的 bean 定义
3,DispatcherServlet 初始化策略: initStrategies
DispatcherServlet 的继承图如下
DispatcherServlet 是一个 Servlet,tomcat 启动过程中会调用其 init 方法, 一串的调用后, 会调用 DispatcherServlet 的 initStrategies 方法
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- initHandlerMappings(context);
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
- initFlashMapManager(context);
- }
实例化步骤 1 中的默认实现, 并填充到 DispatcherServlet 各个属性值中
4,DefaultAnnotationHandlerMapping 的拦截器初始化
DispatcherServlet.properties 种指定了两个默认的 HandlerMapping:BeanNameUrlHandlerMapping,DefaultAnnotationHandlerMapping, 这两者的类继承图如下 (我们暂时只关注 DefaultAnnotationHandlerMapping)
DefaultAnnotationHandlerMapping 间接实现了 ApplicationContextAware, 那么在 DefaultAnnotationHandlerMapping 实例初始化过程中, 会调用 setApplicationContext(ApplicationContext applicationContext) 方法, 一串调用后, 会来到 AbstractUrlHandlerMapping 的 initApplicationContext()
- @Override
- protected void initApplicationContext() throws BeansException {
- extendInterceptors(this.interceptors);
- detectMappedInterceptors(this.mappedInterceptors);
- initInterceptors();
- }
初始化了 DefaultAnnotationHandlerMapping 的拦截器: interceptor
我们来看下具体的初始化过程, 看看上面的顺序是否只是我个人的臆想?
可以看到, 初始化顺序就是我们上面说的, 不是我个人的意淫; 此时的 DefaultAnnotationHandlerMapping 中有我们自定义的 MyInterceptor. 初始化过程我们需要关注的就是上述这些, 下面我们一起看看具体请求的过程
请求的处理
请求从 servlet 的 service 开始, 一路到 DispatcherServlet 的 doDispatch, 如下图
doDispatch
- /**
- * Process the actual dispatching to the handler. 将请求分发到具体的 handler, 也就是我们的 controller
- * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
- * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
- * to find the first that supports the handler class.
- * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
- * themselves to decide which methods are acceptable.
- * @param request current HTTP request
- * @param response current HTTP response
- * @throws Exception in case of any kind of processing failure
- */
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- boolean multipartRequestParsed = false;
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- try {
- ModelAndView mv = null;
- Exception dispatchException = null;
- try {
- processedRequest = checkMultipart(request);
- multipartRequestParsed = processedRequest != request;
- // Determine handler for the current request. 决定哪个 handler 来处理当前的请求
- // mappedHandler 是由 handler 和 interceptor 集合组成的一个执行链, 有点类似 FilterChain
- mappedHandler = getHandler(processedRequest);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request. 决定哪个 adapter 来处理当前的请求
- // handlerMapping 是找出适配的 handler, 而真正回调 handler 的是 adapter
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // Process last-modified header, if supported by the handler.
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is:" + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // handler 的前置处理, 也就是调用适配当前 url 的 interceptor 的 preHandler 方法
- if (!mappedHandler.applyPreHandle(processedRequest, response)) {
- return;
- }
- try {
- // Actually invoke the handler. 真正调用 handler
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- return;
- }
- }
- applyDefaultViewName(request, mv);
- // handler 的后置处理, 也就是调用适配当前 url 的 interceptor 的 postHandler 方法
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- }
- catch (Exception ex) {
- dispatchException = ex;
- }
- // 处理 handler 返回的结果, 会调用适配当前 url 的 interceptor 的 afterCompletion 方法
- // 这里会将响应结果返回给请求者
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- catch (Exception ex) {
- triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
- }
- catch (Error err) {
- triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- // Instead of postHandle and afterCompletion
- mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
- return;
- }
- // Clean up any resources used by a multipart request.
- if (multipartRequestParsed) {
- cleanupMultipart(processedRequest);
- }
- }
- }
- View Code
handlerMapping 具体如何找到匹配当前 url 的 handler(一般而言就是我们的 controller),handlerAdapter 具体如何回调真正的 handler, 有兴趣的可以自行去跟下, 我就不跟了. 我们具体看下 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 这个与我们最初的疑问有关
processDispatchResult
可以看到 model 中的 persons 会被设置到 request 的 attributes 中, 然后转发请求到 show_person.jsp, 转发过程中 request 作用域的变量仍然有效, 所以 show_person.jsp 中的 jstl 标签和 el 表达式能够取到 persons 变量, 最后将 show_person.jsp 中的内容填充好之后的静态内容返回给请求者; 至此就完成了一次请求的响应
问题解答
回到我们开篇的疑问: Spring mvc 是何时, 何地, 如何将 Model 中的属性绑定到哪个作用域? 想必大家已经知道答案了
Controller 中的 model,ModelMap 的注入由 spring mvc 完成, 这个不是请求传入的参数, 用于绑定变量到 Servlet 作用域; 默认情况下, 在 DispatcherServlet 调用了真正的 handler 之后, 将结果返回给请求者的过程中, 将 model,modelMap 中的变量设置到了 request 的 attributes 中, 转发的过程中, request 中的变量仍然有效, 所以 show_person.jsp 中能取到 persons 这个变量, 自此疑问得到解答
总结
1,Spring MVC 工作原理图
图是用的别人的, 具体是谁的我也不记得了 (捂脸)
2,DefaultAnnotationHandlerMapping 在 spring3.2 中被废弃, 替换成了 RequestMappingHandlerMapping
来源: https://www.cnblogs.com/youzhibing/p/10695012.html