|
| 1 | +### 先来看一下什么是 MVC 模式 |
| 2 | + |
| 3 | +MVC 是一种设计模式. |
| 4 | + |
| 5 | +**MVC 的原理图如下:** |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +### SpringMVC 简单介绍 |
| 12 | + |
| 13 | +SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是 DispatcherServlet,它是一个 Servlet,顶层是实现的Servlet接口。 |
| 14 | + |
| 15 | +### SpringMVC 使用 |
| 16 | + |
| 17 | +需要在 web.xml 中配置 DispatcherServlet 。并且需要配置 Spring 监听器ContextLoaderListener |
| 18 | + |
| 19 | +```xml |
| 20 | + |
| 21 | +<listener> |
| 22 | + <listener-class>org.springframework.web.context.ContextLoaderListener |
| 23 | + </listener-class> |
| 24 | +</listener> |
| 25 | +<servlet> |
| 26 | + <servlet-name>springmvc</servlet-name> |
| 27 | + <servlet-class>org.springframework.web.servlet.DispatcherServlet |
| 28 | + </servlet-class> |
| 29 | + <!-- 如果不设置init-param标签,则必须在/WEB-INF/下创建xxx-servlet.xml文件,其中xxx是servlet-name中配置的名称。 --> |
| 30 | + <init-param> |
| 31 | + <param-name>contextConfigLocation</param-name> |
| 32 | + <param-value>classpath:spring/springmvc-servlet.xml</param-value> |
| 33 | + </init-param> |
| 34 | + <load-on-startup>1</load-on-startup> |
| 35 | +</servlet> |
| 36 | +<servlet-mapping> |
| 37 | + <servlet-name>springmvc</servlet-name> |
| 38 | + <url-pattern>/</url-pattern> |
| 39 | +</servlet-mapping> |
| 40 | + |
| 41 | +``` |
| 42 | + |
| 43 | +### SpringMVC 工作原理(重要) |
| 44 | + |
| 45 | +**简单来说:** |
| 46 | + |
| 47 | +客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Moder)->将得到视图对象返回给用户 |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | +**如下图所示:** |
| 52 | + |
| 53 | + |
| 54 | +上图的一个笔误的小问题:Spring MVC 的入口函数也就是前端控制器 DispatcherServlet 的作用是接收请求,响应结果。 |
| 55 | + |
| 56 | +**流程说明(重要):** |
| 57 | + |
| 58 | +(1)客户端(浏览器)发送请求,直接请求到 DispatcherServlet。 |
| 59 | + |
| 60 | +(2)DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。 |
| 61 | + |
| 62 | +(3)解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。 |
| 63 | + |
| 64 | +(4)HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑。 |
| 65 | + |
| 66 | +(5)处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。 |
| 67 | + |
| 68 | +(6)ViewResolver 会根据逻辑 View 查找实际的 View。 |
| 69 | + |
| 70 | +(7)DispaterServlet 把返回的 Model 传给 View(视图渲染)。 |
| 71 | + |
| 72 | +(8)把 View 返回给请求者(浏览器) |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +### SpringMVC 重要组件说明 |
| 77 | + |
| 78 | + |
| 79 | +**1、前端控制器DispatcherServlet(不需要工程师开发),由框架提供(重要)** |
| 80 | + |
| 81 | +作用:**Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。** |
| 82 | + |
| 83 | +**2、处理器映射器HandlerMapping(不需要工程师开发),由框架提供** |
| 84 | + |
| 85 | +作用:根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler即处理器(Controller),SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 |
| 86 | + |
| 87 | +**3、处理器适配器HandlerAdapter** |
| 88 | + |
| 89 | +作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler |
| 90 | +通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 |
| 91 | + |
| 92 | +**4、处理器Handler(需要工程师开发)** |
| 93 | + |
| 94 | +注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler |
| 95 | +Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 |
| 96 | +由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。 |
| 97 | + |
| 98 | +**5、视图解析器View resolver(不需要工程师开发),由框架提供** |
| 99 | + |
| 100 | +作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) |
| 101 | +View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。 |
| 102 | +一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。 |
| 103 | + |
| 104 | +**6、视图View(需要工程师开发)** |
| 105 | + |
| 106 | +View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...) |
| 107 | + |
| 108 | +**注意:处理器Handler(也就是我们平常说的Controller控制器)以及视图层view都是需要我们自己手动开发的。其他的一些组件比如:前端控制器DispatcherServlet、处理器映射器HandlerMapping、处理器适配器HandlerAdapter等等都是框架提供给我们的,不需要自己手动开发。** |
| 109 | + |
| 110 | +### DispatcherServlet详细解析 |
| 111 | + |
| 112 | +首先看下源码: |
| 113 | + |
| 114 | +```java |
| 115 | +package org.springframework.web.servlet; |
| 116 | + |
| 117 | +@SuppressWarnings("serial") |
| 118 | +public class DispatcherServlet extends FrameworkServlet { |
| 119 | + |
| 120 | + public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; |
| 121 | + public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; |
| 122 | + public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; |
| 123 | + public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; |
| 124 | + public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; |
| 125 | + public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; |
| 126 | + public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; |
| 127 | + public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; |
| 128 | + public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager"; |
| 129 | + public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; |
| 130 | + public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; |
| 131 | + public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; |
| 132 | + public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; |
| 133 | + public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; |
| 134 | + public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; |
| 135 | + public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; |
| 136 | + public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION"; |
| 137 | + public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; |
| 138 | + private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; |
| 139 | + protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); |
| 140 | + private static final Properties defaultStrategies; |
| 141 | + static { |
| 142 | + try { |
| 143 | + ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); |
| 144 | + defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); |
| 145 | + } |
| 146 | + catch (IOException ex) { |
| 147 | + throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + /** Detect all HandlerMappings or just expect "handlerMapping" bean? */ |
| 152 | + private boolean detectAllHandlerMappings = true; |
| 153 | + |
| 154 | + /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */ |
| 155 | + private boolean detectAllHandlerAdapters = true; |
| 156 | + |
| 157 | + /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */ |
| 158 | + private boolean detectAllHandlerExceptionResolvers = true; |
| 159 | + |
| 160 | + /** Detect all ViewResolvers or just expect "viewResolver" bean? */ |
| 161 | + private boolean detectAllViewResolvers = true; |
| 162 | + |
| 163 | + /** Throw a NoHandlerFoundException if no Handler was found to process this request? **/ |
| 164 | + private boolean throwExceptionIfNoHandlerFound = false; |
| 165 | + |
| 166 | + /** Perform cleanup of request attributes after include request? */ |
| 167 | + private boolean cleanupAfterInclude = true; |
| 168 | + |
| 169 | + /** MultipartResolver used by this servlet */ |
| 170 | + private MultipartResolver multipartResolver; |
| 171 | + |
| 172 | + /** LocaleResolver used by this servlet */ |
| 173 | + private LocaleResolver localeResolver; |
| 174 | + |
| 175 | + /** ThemeResolver used by this servlet */ |
| 176 | + private ThemeResolver themeResolver; |
| 177 | + |
| 178 | + /** List of HandlerMappings used by this servlet */ |
| 179 | + private List<HandlerMapping> handlerMappings; |
| 180 | + |
| 181 | + /** List of HandlerAdapters used by this servlet */ |
| 182 | + private List<HandlerAdapter> handlerAdapters; |
| 183 | + |
| 184 | + /** List of HandlerExceptionResolvers used by this servlet */ |
| 185 | + private List<HandlerExceptionResolver> handlerExceptionResolvers; |
| 186 | + |
| 187 | + /** RequestToViewNameTranslator used by this servlet */ |
| 188 | + private RequestToViewNameTranslator viewNameTranslator; |
| 189 | + |
| 190 | + private FlashMapManager flashMapManager; |
| 191 | + |
| 192 | + /** List of ViewResolvers used by this servlet */ |
| 193 | + private List<ViewResolver> viewResolvers; |
| 194 | + |
| 195 | + public DispatcherServlet() { |
| 196 | + super(); |
| 197 | + } |
| 198 | + |
| 199 | + public DispatcherServlet(WebApplicationContext webApplicationContext) { |
| 200 | + super(webApplicationContext); |
| 201 | + } |
| 202 | + @Override |
| 203 | + protected void onRefresh(ApplicationContext context) { |
| 204 | + initStrategies(context); |
| 205 | + } |
| 206 | + |
| 207 | + protected void initStrategies(ApplicationContext context) { |
| 208 | + initMultipartResolver(context); |
| 209 | + initLocaleResolver(context); |
| 210 | + initThemeResolver(context); |
| 211 | + initHandlerMappings(context); |
| 212 | + initHandlerAdapters(context); |
| 213 | + initHandlerExceptionResolvers(context); |
| 214 | + initRequestToViewNameTranslator(context); |
| 215 | + initViewResolvers(context); |
| 216 | + initFlashMapManager(context); |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +``` |
| 221 | + |
| 222 | +DispatcherServlet类中的属性beans: |
| 223 | + |
| 224 | +- HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。 |
| 225 | +- HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序。- - - |
| 226 | +- ViewResolver:根据实际配置解析实际的View类型。 |
| 227 | +- ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局。 |
| 228 | +- MultipartResolver:解析多部分请求,以支持从HTML表单上传文件。- |
| 229 | +- FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向。 |
| 230 | + |
| 231 | +在Web MVC框架中,每个DispatcherServlet都拥自己的WebApplicationContext,它继承了ApplicationContext。WebApplicationContext包含了其上下文和Servlet实例之间共享的所有的基础框架beans。 |
| 232 | + |
| 233 | +**HandlerMapping** |
| 234 | + |
| 235 | + |
| 236 | + |
| 237 | +HandlerMapping接口处理请求的映射HandlerMapping接口的实现类: |
| 238 | + |
| 239 | +- SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。 |
| 240 | +- DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类。 |
| 241 | + |
| 242 | +**HandlerAdapter** |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | + |
| 247 | +HandlerAdapter接口-处理请求映射 |
| 248 | + |
| 249 | +AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上。 |
| 250 | + |
| 251 | +**HandlerExceptionResolver** |
| 252 | + |
| 253 | + |
| 254 | + |
| 255 | + |
| 256 | +HandlerExceptionResolver接口-异常处理接口 |
| 257 | + |
| 258 | +- SimpleMappingExceptionResolver通过配置文件进行异常处理。 |
| 259 | +- AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。 |
| 260 | + |
| 261 | +**ViewResolver** |
| 262 | + |
| 263 | + |
| 264 | + |
| 265 | +ViewResolver接口解析View视图。 |
| 266 | + |
| 267 | +UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。 |
0 commit comments