0x5 Spring系列:用SpringMVC工作原理详解

举报
云享专家 发表于 2019/09/19 16:39:45 2019/09/19
【摘要】 DispatcherServlet的类中的属性豆: · HandlerMapping的:用于处理程序映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。 · HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是哪个处理程序.- -

先来看一下什么是MVC模式

MVC是一种设计模式。

MVC的原理图如下:

1.jpg

 

SpringMVC简单介绍

SpringMVC框架是以请求为驱动,围绕Servlet设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是DispatcherServlet,它是一个Servlet,顶层是实现的Servlet接口。

SpringMVC使用

需要在web.xml中配置DispatcherServlet。并且需要配置Spring监听器ContextLoaderListener

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    
<!-- 如果不设置init-param标签,则必须在/WEB-INF/下创建xxx-servlet.xml文件,其中xxxservlet-name中配置的名称。 -->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>


SpringMVC工作原理(重要)

简单来说:

客户端发送请求 - >前端控制器DispatcherServlet接受客户端请求 - >找到处理器映射HandlerMapping解析请求对应的Handler-> HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑 - >处理器返回一个模型视图ModelAndView - >视图解析器进行解析 - >返回一个视图对象 - >前端控制器DispatcherServlet渲染数据(Moder) - >将得到视图对象返回给用户

如下图所示:

2.jpg

 

上图的一个笔误的小问题:Spring MVC的入口函数也就是前端控制器DispatcherServlet的作用是接收请求,响应结果。

流程说明(重要):

(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。

(2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。

(3)解析到对应的Handler(也就是我们平常说的Controller Control制器)后,开始由HandlerAdapter适配器处理。

(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。

(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。

(6)ViewResolver会根据逻辑查看查找实际的查看。

(7)DispaterServlet把返回的模型传给View(视图渲染)。

(8)把View返回给请求者(浏览器)

SpringMVC重要组件说明

如图1所示,前端控制器的DispatcherServlet(不需要工程师开发),由框架提供(重要)

作用:Spring MVC的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的C,DispatcherServlet的是整个流程控制的中心,由它调用其它组件处理用户的请求;分发器的存在降低了组件之间的耦合性。

2,处理器映射器的HandlerMapping(不需要工程师开发),由框架提供

作用:根据请求的URL查找Handler.HandlerMapping负责根据用户请求找到处理程序即处理器(控制器),用SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3,处理器适配器的HandlerAdapter

作用:按照特定规则(的HandlerAdapter要求的规则)去执行处理程序
通过的HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

如图4所示,处理器处理程序(需要工程师开发)

注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发处理程序。

5,视图解析器查看resolver(不需要工程师开发),由框架提供

作者:进行视图解析,根据逻辑视图名解析成真正的视图(查看)
查看Resolver负责将处理结果生成查看视图,查看解析器首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成查看视图对象,最后对查看进行渲染将处理结果通过页面展示给用户.springmvc框架提供了很多的查看视图类型,包括:jstlView,freemarkerView,pdfView等。
一般情况下需要通过页面标签或页面模型技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6,视图视图(需要工程师开发)

查看是一个接口,实现类支持不同的视图类型(JSP,FreeMarker的,PDF ...)

注意:处理器处理程序(也就是我们平常说的控制器控制器)以及视图层视图都是需要我们自己手动开发的其他的一些组件比如:前端控制器的DispatcherServlet,处理器映射器的HandlerMapping,处理器适配器的HandlerAdapter等等都是框架提供给我们的,不需要自己手动开发。

DispatcherServlet的详细解析

首先看下源码:

package org.springframework.web.servlet;

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
    
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
    
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
    
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
    
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
    
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
    
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
    
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
    
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
    
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
    
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
    
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
    
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
    
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
    
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
    
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
    
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
    
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
    
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
    
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
    
private static final Properties defaultStrategies;
    
static {
        
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());
        }
    }

    
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
    
private boolean detectAllHandlerMappings = true;

    
/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
    
private boolean detectAllHandlerAdapters = true;

    
/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
    
private boolean detectAllHandlerExceptionResolvers = true;

    
/** Detect all ViewResolvers or just expect "viewResolver" bean? */
    
private boolean detectAllViewResolvers = true;

    
/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
    
private boolean throwExceptionIfNoHandlerFound = false;

    
/** Perform cleanup of request attributes after include request? */
    
private boolean cleanupAfterInclude = true;

    
/** MultipartResolver used by this servlet */
    
private MultipartResolver multipartResolver;

    
/** LocaleResolver used by this servlet */
    
private LocaleResolver localeResolver;

    
/** ThemeResolver used by this servlet */
    
private ThemeResolver themeResolver;

    
/** List of HandlerMappings used by this servlet */
    
private List<HandlerMapping> handlerMappings;

    
/** List of HandlerAdapters used by this servlet */
    
private List<HandlerAdapter> handlerAdapters;

    
/** List of HandlerExceptionResolvers used by this servlet */
    
private List<HandlerExceptionResolver> handlerExceptionResolvers;

    
/** RequestToViewNameTranslator used by this servlet */
    
private RequestToViewNameTranslator viewNameTranslator;

    
private FlashMapManager flashMapManager;

    
/** List of ViewResolvers used by this servlet */
    
private List<ViewResolver> viewResolvers;

    
public DispatcherServlet() {
        
super();
    }

    
public DispatcherServlet(WebApplicationContext webApplicationContext) {
        
super(webApplicationContext);
    }
    
@Override
    
protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    
protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
}


DispatcherServlet的类中的属性豆:

·      HandlerMapping的:用于处理程序映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。

·      HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是哪个处理程序.- - -

·      视图解析器:根据实际配置解析实际的视图类型。

·      ThemeResolver的:解决网络应用程序可以使用的主题,例如提供个性化布局。

·      MultipartResolver:解析多部分请求,以支持从HTML表单上传文件.-

·      FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的输入和输出的FlashMap,通常用于重定向。

在Web MVC框架中,每个DispatcherServlet都拥有自己的WebApplicationContext,它继承了ApplicationContext.WebApplicationContext包含了其上下文和Servlet实例之间共享的所有的基础框架bean。

的HandlerMapping

3.jpg

 

HandlerMapping的接口处理请求的映射的HandlerMapping接口的实现类:

·      SimpleUrlHandlerMapping建立类通过配置文件把URL映射到控制器类。

·      DefaultAnnotationHandlerMapping类通过注解把URL映射到控制器类。

的HandlerAdapter

4.jpg

的HandlerAdapter接口 - 处理请求映射

AnnotationMethodHandlerAdapter上:通过注解,把请求URL映射到控制器类的方法上。

HandlerExceptionResolver

5.jpg

HandlerExceptionResolver接口 - 异常处理接口

·      的SimpleMappingExceptionResolver通过配置文件进行异常处理。

·      AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。

视图解析器

6.jpg

 

ViewResolver的接口解析查看视图。

UrlBasedViewResolver类通过配置文件,把一个视图名交给到一个查看来处理。

!本文整理自网络,原文出处暂不知,对原文做了较大的改动,在此说明整理人:JAVA团长


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。