SpringMVC深入(组件篇)

简介: 1、组件说明DispatcherServlet:中央控制器,前端控制器用户请求到达前端控制器(dispatcherServlet),他是整个流程控制的中心,由它负责调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。这玩意可以理解成一个【咨询处】,你去某个地方办事,先去咨询处问问我们应该先干什么,等第一件事做完了,可以接着去咨询处咨询,你的下一步工作应该是什么。handler:处理器Handler也叫后端控制器,在DispatcherServlet的控制下H

1、组件说明

DispatcherServlet:中央控制器,前端控制器

用户请求到达前端控制器(dispatcherServlet),他是整个流程控制的中心,由它负责调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

这玩意可以理解成一个【咨询处】,你去某个地方办事,先去咨询处问问我们应该先干什么,等第一件事做完了,可以接着去咨询处咨询,你的下一步工作应该是什么。

handler:处理器

Handler也叫后端控制器,在DispatcherServlet的控制下Handler对【具体的用户请求】进行处理,由于Handler涉及到【具体的用户业务请求】,所以一般情况需要程序员【根据业务需求开发Handler】。这玩意就是你写的controller,别把他想成啥高级玩意,你也能写个处理器。

View:视图

一般情况下,需要通过【页面标签或页面模版技术】将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。目前我们接触过得视图技术就是jsp,当然还有Freemarker,Thymeleaf等。

HandlerMapping:处理器映射器

HandlerMapping负责根据【用户请求url】找到【Handler】即处理器,springmvc提供了不同的【处理器映射器】实现,如配置文件方式,实现接口方式,注解方式等。

HandlAdapter:处理器适配器

HandlerAdapter负责调用具体的处理器,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。我们写的controller中的方法,将来就是会由处理器适配器调用。

ViewResolver:视图解析器

View Resolver负责将处理结果生成View视图,View Resolver首先根据【逻辑视图名】解析成【物理视图名】即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

2、执行流程

Springmvc的是围绕DispatcherServlet进行设计的:

DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。

Spring MVC框架像许多其他MVC框架一样, 以【请求为驱动】 , 围绕一个【核心Servlet】进行请求分派及提供其他功能,DispatcherServlet仅仅是一个的Servlet(它继承自HttpServlet)。

分发的流程大致如下:
在这里插入图片描述
我们甚至可以大致看一下源码:

众所周知,servlet中的核心方法是【service方法】,当请求一个servlet时会主动调用service方法,而在DispatcherServlet的service方法中,其核心时调用了一个doDispatch的方法,如下:

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);

                // 获得一个过滤器链,这就是处理器适配器的工作
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
   
   
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 确定当前请求的处理程序适配器   
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 省略一些
                ...
                // 处理器链调用所有拦截器的前置处理程序,如有不满足的直接返回:
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   
   
                    return;
                }

                // 此处由处理器适配器调用我们写的controller。
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
   
   
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                // 处理器链调用所有拦截器的后置处理程序,如有不满足的直接返回:
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
   
   
                dispatchException = ex;
            }
            catch (Throwable err) {
   
   
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 处理最终结果,视图解析器处理mv,还要做统一的异常处理
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

关于拦截器,可以看一下这个接口

public interface HandlerInterceptor {
   
   
    //处理器执行之前
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   
   
        return true;
    }
    //处理器执行之后
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
   
   
    }
    //完成之后
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
   
   
    }
}

其实这个处理过程简单一点回答总结如下:

1.通过url匹配一个过滤器链,其中包含多个过滤器和一个处理器
2.第一步调用拦截器的preHandle方法
3.第二步执行handler方法
4.第三部调用拦截器的postHandle方法
5.将结果给视图解析器进行处理
6.处理完成后调用afterCompletion

3、三个上下文

在我们的web项目中存在至少三个上下文,分别是【servlet上下文】,【spring上下文】以及【springmvc上下文】,具体如下:
在这里插入图片描述

(1)ServletContext

对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是我们的ServletContext,其为后面的spring IoC容器提供一个宿主环境。

(2)spring上下文

在web.xml的配置中,我们需要提供一个监听器【ContextLoaderListener】。在web容器启动时,会触发【容器初始化】事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用。

在这个方法中,spring会初始化一个【上下文】,这个上下文被称为【根上下文】,即【WebApplicationContext】,这是一个接口类,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的【context-param】配置指定,默认配置文件为【/WEB-INF/applicationContext.xml】。
在这个IoC容器初始化完毕后,spring以【WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE】为属性Key,将其存储到ServletContext中,便于将来获取;
在这里插入图片描述
相关配置:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/app-context.xml</param-value>
</context-param>

(3)springmvc上下文

contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,通常只配置一个,以最常见的DispatcherServlet为例,这个servlet实际上是一个【标准的前端控制器】,用以转发、匹配、处理每个servlet请求。

DispatcherServlet在初始化的时候会建立自己的IoC上下文,用以持有【spring mvc相关的bean】。在建立DispatcherServlet自己的IoC上下文时,会利用【WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE】先从ServletContext中获取之前的【根上下文】作为自己上下文的【parent上下文】。有了这个parent上下文之后,再初始化自己持有的上下文,这个上下文本质上也是XmlWebApplicationContext,默认读取的配置文件是【/WEB-INF/springmvc-servlet.xml】,当然我们也可以使用init-param标签的【contextConfigLocation属性】进行配置。

DispatcherServlet初始化自己上下文的工作在其【initStrategies】方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是xmlWebApplicationContext。初始化完毕后,spring以【"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称】为Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet还可以共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。
注:springMVC容器只负责创建Controller对象,不会创建service和dao,并且他是一个子容器。而spring的容器只负责Service和dao对象,是一个父容器。子容器可以看见父容器的对象,而父容器看不见子容器的对象,这样各司其职。

我们可以通过debug,使用ServletContext servletContext = req.getServletContext()查方法看ServletContext,如下:
在这里插入图片描述

目录
相关文章
|
XML 缓存 Java
SpringMVC常见组件之ViewResolver分析
本文我们尝试总结分析SpringMVC体系中的视图解析器-ViewResolver。其根据name解析视图View,通常鼓励实现类考虑国际化策略实现。
110 0
|
7月前
|
XML 存储 Java
SpringMVC常见组件之HandlerMapping分析
SpringMVC常见组件之HandlerMapping分析
159 0
|
7月前
|
XML Java 应用服务中间件
SpringMVC与Servlet3.0整合 - ServletContainerInitializer注解配置项目
SpringMVC与Servlet3.0整合 - ServletContainerInitializer注解配置项目
57 1
|
内存技术
SpringMVC常见组件之HandlerMethodReturnValueHandler解析-2
SpringMVC常见组件之HandlerMethodReturnValueHandler解析-2
79 0
SpringMVC常见组件之HandlerMethodReturnValueHandler解析-1
SpringMVC常见组件之HandlerMethodReturnValueHandler解析-1
96 0
|
前端开发 Java 应用服务中间件
SpringMVC(基于Spring 的Web 层MVC 框架)--SpingMVC 执行流程--@RequestMapping的使用
SpringMVC(基于Spring 的Web 层MVC 框架)--SpingMVC 执行流程--@RequestMapping的使用
117 0
|
前端开发 Java
SpringMVC 各组件功能
SpringMVC 各组件功能
72 0
SpringMVC学习(七):SpringMVC的视图
SpringMVC学习(七):SpringMVC的视图
135 0
SpringMVC学习(七):SpringMVC的视图
|
前端开发 Java Spring
【SpringMVC 从 0 开始】SpringMVC 的视图
【SpringMVC 从 0 开始】SpringMVC 的视图
【SpringMVC 从 0 开始】SpringMVC 的视图
|
Java
springMvc44-springMVC的三大组件
springMvc44-springMVC的三大组件
99 0
springMvc44-springMVC的三大组件