(二)SpringMvc 工作流程 以及 核心组件

(二)SpringMVC 工作流程 以及 核心组件

 

 

 

1、SpringMVC 工作流程

面试时,关于 99% 都是这个问题。

流程编排的 java 开源项目 流程组件 java_流程编排的 java 开源项目

请求流程:

  • 1、用户发送请求至前端控制器DispatcherServlet。
  • 2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  • 3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  • 4、 DispatcherServlet调用HandlerAdapter处理器适配器。
  • 5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  • 6、Controller执行完成返回ModelAndView。
  • 7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
  • 8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  • 9、ViewReslover解析后返回具体View.
  • 10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。 
  • 11、DispatcherServlet响应用户。

2、SpringMVC 中的组件

2.1、DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于 mvc 模式中的c,DispatcherServlet 是整个流程控制的中心,相当于是 SpringMVC 的大脑,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

2.2、HandlerMapping:处理器映射器

HandlerMapping 负责根据用户请求找到 Handler 即处理器(也就是我们所说的 Controller),SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等,在实际开发中,我们常用的方式是注解方式。

2.3、Handler:处理器

Handler 是继DispatcherServlet 前端控制器的后端控制器,在DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。由于Handler 涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发 Handler。(这里所说的 Handler 就是指我们的 Controller)

2.4、HandlAdapter:处理器适配器

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

2.5、ViewResolver:视图解析器

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

 

3、组件详解:

  • Spring MVC 的 三大组件,它们分别是处理器映射器(HandlerMapper),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)

3.1、 处理器映射器

作用:

     通过处理器映射,你可以将Web 请求映射到正确的处理器 Controller 上。当接收到请求时,DispactherServlet 将请求交给 HandlerMapping 处理器映射,让他检查请求并找到一个合适的HandlerExecutionChain,这个HandlerExecutionChain 包含一个能处理该请求的处理器 Controller。然后,DispactherServlet 执行在HandlerExecutionChain 中的处理器 Controller。

Spring内置了许多处理器映射策略,目前主要由三个实现。SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping 和    RequestMappingHandlerMapping。他们都实现了 HandlerMapping 接口

流程编排的 java 开源项目 流程组件 java_xml_02

注意:Spring MVC3.1之前使用DefaultAnnotationHandlerMapping,Spring MVC3.1之后改为RequestMappingHandlerMapping。

1)SimpleUrlHandlerMapping

SimpleUrlHandlerMapping 在应用上下文中可以进行配置,并且有Ant 风格的路径匹配功能。例如我们在 springmvc.xml 中配置一个SimpleUrlHandlerMapping 处理器映射。

springmvc.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.创建SimpleUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello">helloController</prop>
            </props>
        </property>
    </bean>

    <!--2.创建Controller对象-->
    <bean id="helloController" class="com.dw.study.controller.HelloController"/>

    <!--3.视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

对应的HelloController类:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 控制器
 */
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView("success");
        return mv;
    }
}

 

2)BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping 将收到的Http请求映射到bean的名字上。如:我们用如下方式将包含http://localhost:8080/hello.do的访问请求映射到指定的HelloController上。

springmvc.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.创建BeanNameUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--2.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
    <bean id="/hello" class="com.dw.study.controller.HelloController"/>

    <!--3.视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

 

Controller类

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 控制器
 */
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView("success");
        return mv;
    }
}

注意:在bean的id中要加上斜杆,Controller的代码跟前面的SimpleUrlHandlerMapping一样,实现Controller,重写handlerRequest()方法即可。

在默认情况下,如果没有在上下文中没有找到处理器映射,DispactherServlet 会为你创建一个BeanNameUrlHandlerMapping。

 

3)RequestMappingHandlerMapping

RequestMappingHandlerMapping是三个中最常用的HandlerMapping,因为注解方式比较通俗易懂,代码界面清晰,只需要在代码前加上@RequestMapping()的相关注释就可以了。代码示例如下:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * 控制器
 */
@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("进入控制器的方法");

        //注意:这里返回的只是页面名称,不是完整的页面访问路径
        return "success";
    }
}

 

springmvc.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1.扫描Controller的包-->
    <context:component-scan base-package="com.dw.study.controller"/>

    <!-- 2.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 2.1 页面前缀 -->
        <property name="prefix" value="/pages/"/>
        <!-- 2.2 页面后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3.创建RequestMappingHandlerMapping对象-->
    <mvc:annotation-driven/>
</beans>

注意:重点是添加 :   <mvc:annotation-driven/> 标签!

3.2 、处理器适配器

作用:

     HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当HandlerMapping获取到执行请求的Controller时,DispatcherServlte会根据Controller对应的Controller类型来调用相应的HandlerAdapter来进行处理。

HandlerAdapter 的实现有 HttpRequestHandlerAdapterSimpleServletHandlerAdapterSimpleControllerHandlerAdapterAnnotationMethodHandlerAdapter(Spring MVC 3.1后已废弃)和RequestMappingHandlerAdapter

 

流程编排的 java 开源项目 流程组件 java_spring_03

 

 

1)HttpRequestHandlerAdapter

HttpRequestHandlerAdapter可以处理类型为HttpRequestHandler的handler,对handler的处理是调用HttpRequestHandler的handleRequest()方法。

Controller类:

import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 控制器
 */
public class HelloController implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("Hello-springMvc");
    }
}

springmvc.xml配置,创建HttpRequestHandlerAdapter对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.创建BeanNameUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--2.创建HttpRequestHandlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

    <!--3.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
    <bean id="/hello" class="com.dw.study.controller.HelloController"/>

</beans>

 

2)SimpleServletHandlerAdapter

SimpleServletHandlerAdapter可以处理类型为Servlet,就是把Servlet当做Controller来处理,使用Servlet的service方法处理用户请求。

springmvc.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.创建BeanNameUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--2.创建SimpleServletHandlerAdapter-->
    <bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>

    <!--3.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
    <bean id="/hello" class="com.dw.study.controller.HelloServlet"/>

</beans>

 Controller类:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 把Servlet作为控制器
 */
public class HelloServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Hello-springMvc");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req,resp);
    }
}

 

3)SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter可以处理类为Controller的控制器,使用Controller的handlerRequest方法处理用户请求。

springmvc.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.创建BeanNameUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--2.创建SimpleControllerHandlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!--3.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
    <bean id="/hello" class="com.dw.study.controller.HelloController"/>

</beans>

 

Controller类

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 控制器
 */
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
        response.getWriter().write("Hello-springMvc");
        return null;
    }
}

 

4)RequestMappingHandlerAdapter

RequestMappingHandlerAdapter可以处理类型为HandlerMethod的控制器,通过Java反射调用HandlerMethod的方法来处理用户请求。

springmvc.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.扫描Controller,创建Controller对象 -->
    <context:component-scan base-package="com.dw.study.controller"/>

    <!--2.创建RequestMappingHandlerMapping-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!--3.创建RequestMappingHandlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

</beans>

 

Controller类

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 控制器
 */
@Controller
public class HelloController{
    @RequestMapping("/hello.do")
    public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException {
        response.getWriter().write("Hello-springMvc");
    }
}

 

3 、视图解析器

 作用:

Spring MVC中的视图解析器的主要作用就是将逻辑视图转换成用户可以看到的物理视图。

当用户对SpringMVC应用程序发起请求时,这些请求都会被Spring MVC的DispatcherServlet处理,通过处理器找到最为合适的HandlerMapping定义的请求映射中最为合适的映射,然后通过HandlerMapping找到相对应的Handler,然后再通过相对应的HandlerAdapter处理该Handler。返回结果是一个ModelAndView对象,当该ModelAndView对象中不包含真正的视图,而是一个逻辑视图路径的时候,ViewResolver就会把该逻辑视图路径解析为真正的View视图对象,然后通过View的渲染,将最终结果返回给用户。

SpringMVC中处理视图最终要的两个接口就是ViewResolverView,ViewResolver的作用是将逻辑视图解析成物理视图,View的主要作用是调用其render()方法将物理视图进行渲染。

3.2 常见的视图解析器

以下为Spring MVC提供常见视图解析器:

视图类型

说明

BeanNameViewResolver

将逻辑视图名称解析为一个Bean,Bean的Id等于逻辑视图名

InternalResourceViewResolver

将视图名解析为一个URL文件,一般使用该解析器将视图名映射为一个保存在WEB-INF目录下的程序文件,如 JSP

JaperReportsViewResolver

JapserReports是基于Java的开源报表工具,该解析器解析为报表文件对应的URL

FreeMarkerViewResolver

解析为基于FreeMarker模板的模板文件

VelocityViewResolver

解析为Velocity模板技术的模板文件

VelocityLayoutViewResolver

解析为Velocity模板技术的模板文件

其中,最常用的是  InternalResourceViewResolver,配置如下:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <!-- 页面前缀 -->
   <property name="prefix" value="/pages/"/>
    <!--页面后缀 -->
   <property name="suffix" value=".jsp"/>
</bean>