当前位置: 代码迷 >> 综合 >> Spring MVC系列之(五)、Spring MVC的前端控制器(DispatcherServlet)工作流程及原理
  详细解决方案

Spring MVC系列之(五)、Spring MVC的前端控制器(DispatcherServlet)工作流程及原理

热度:29   发布时间:2023-11-24 10:59:03.0

五、Spring MVC的工作流程执行图

在这里插入图片描述

5.1 DispatcherServlet流程中每一步的含义

  1. 用户通过浏览器向服务器发送请求。请求会被Spring MVC的DispatcherServlet(前端控制器)所拦截
  2. DispatcherServlet拦截到请求后,会通过HandlerMapping处理器映射器
  3. 处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
  4. DispatcherServlet会通过返回信息选择合适的HandlerAdapter(处理器适配器)
  5. HandlerAdpater会调用并执行Handler(处理器)这里的处理器指的就是程序中Controller类,也被称为后端控制器;
  6. Controller执行后会返回一个ModelAndView对象,该对象中会包含视图名或包含模型视图名
  7. HandlerAdapter将ModelAndView对象返回给DispatcherServlet;
  8. DispatcherServlet会根据ModelAndView对象选择一个合适ViewResolver(视图解析器)
  9. ViewResolver解析后,会向DispatcherServlet中返回一个具体的View(视图)
  10. DispatcherServlet对View进行渲染(即将模型数据填充至视图中);
  11. 视图渲染结果会返回给客户端浏览器显示。

5.2 DispatcherServlet前端控制器的内部工作原理机制

我们要清楚DispatcherServlet是怎样拦截到我们在浏览器地址栏中发出的请求,以及通过我们的请求找到控制器类中对应的请求方法,并获取到方法的返回值,通过视图解析器找到对应的视图,最终通过请求转发跳转到我们想要请求的页面中。

  • web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>Archetype Created Web Application</display-name><!--配置servlet调度程序--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--启动tomcat服务器时,默认加载applicationContext-springmvc.xml配置文件--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath*:applicationContext-springmvc.xml</param-value></init-param><!--表示容器在启动时立即加载Servlet(此处值越小,加载优先级越高)--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
    

我们知道在web.xml文件中配置前端控制器,但是内部原理是怎么实现的,下面可以通过一个简单的例子来演示下原理,我们不使用系统提供的前端控制器类,我们自己
新建一个DispatcherServlet类。

使用maven创建一个springmvc-DispatcherServlet-principle的Web项目,只需导入javax.servlet.api的依赖文件:

  • pom.xml

    <!--servlet-api-->
    <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>compile</scope>
    </dependency>
    
  • DispatcherServlet类(要继承HttpServlet类)

    package com.servlet;import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;/*** @author 一宿君(CSDN : qq_52596258)* @date 2021-07-25 15:04:05*/
    public class DispatcherServlet extends HttpServlet {
          public DispatcherServlet() {
          System.out.println("构造方法");}@Overridepublic void init() throws ServletException {
          System.out.println("初始化方法---作用是为了存储通过注解扫描controller类下所有@RequestMapping注解标注的value值(请求名)");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          doPost(req,resp);}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          doPost(req,resp);}}
    
  • springmvc-config.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:context="http://www.springframework.org/schema/context"xmlns:bean="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"></beans>
    
  • web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>Archetype Created Web Application</display-name><!--配置servlet调度程序--><servlet><servlet-name>springmvc</servlet-name><servlet-class>com.servlet.DispatcherServlet</servlet-class><!--启动tomcat服务器时,默认加载springmvc-config.xml配置文件--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath*:springmvc-config.xml</param-value></init-param><!--表示容器在启动时立即加载Servlet(此处值越小,加载优先级越高)--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
    

    在这里插入图片描述

  • 创建UserControler控制器类

    package com.controller;/*** @author 一宿君(CSDN : qq_52596258)* @date 2021-07-25 15:11:26*/
    public class UserController {
          //请求到达登录界面public String toLogin(){
          return "login";}//请求到达注册界面public String toRegister(){
          return "register";}//请求到达删除界面public String toDelete(){
          return "delete";}
    }
    

在webapp目录下建三个页面:
在这里插入图片描述

  • login.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>Title</title>
    </head>
    <body>
    <h1>这是login.jsp页面</h1>
    </body>
    </html>
    
  • register.jsp页面
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>Title</title>
    </head>
    <body>
    <h1>这是register.jsp页面</h1>
    </body>
    </html>
    
  • delete.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>Title</title>
    </head>
    <body>
    <h1>这是delete.jsp页面</h1>
    </body>
    </html>
    
    到这一步我们先将项目部署到tomcat服务器上,先保证运行正常!
    在这里插入图片描述
    没问题,进行下一步操作!

5.3 Spring MVC注解驱动原理

  • springmvc.xml

    <!--开启springmvc注解驱动-->
    <mvc:annotation-driven/>
    <!--指定springmvc扫描哪个包-->
    <context:component-scan base-package="com.controller"/><!--内部资源视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--扫描以.jsp为后缀的jsp页面--><property name="suffix" value=".jsp"/><!--扫描/WEB-INF下的所有jsp--><property name="prefix" value="/WEB-INF/jsp/"/>
    </bean>
    

    在这里插入图片描述
    开启注解驱动,我们就无需关注前端控制器是如何帮我们找到相应的视图以及解析,但是原理我们还是要清楚的,如下重点:

  • DispatcherServlet类中init()初始化方法:

    /*** 用于存储请求访问名字*/
    Map<String,String> mappings = new HashMap<>();@Overridepublic void init() throws ServletException {
          System.out.println("初始化方法---作用是为了存储通过注解扫描controller类下所有@RequestMapping注解标注的value值(请求名)");mappings.put("toLogin","com.controller.UserController.toLogin");mappings.put("toRegister","com.controller.UserController.toRegister");mappings.put("toDelete","com.controller.UserController.toDelete");}
    

在这里插入图片描述

  • DispatcherServlet类中的doGet方法

     @Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          //1 获取请求URIString requestURI = req.getRequestURI();System.out.println(requestURI);//2 获取请求URI名称(从最后一个/处,加1截取)String requestURIName = requestURI.substring(requestURI.lastIndexOf("/") + 1);System.out.println(requestURIName);
    }
    

    在这里插入图片描述

  • 到这步我们运行下项目,在地址栏输入toLogin,看控制台:
    在这里插入图片描述

    在这里插入图片描述

当我们得到请求地址名后,再去map集合中进行对比,查看我们事先初始化时(此处是模拟springmvc通过开启注解扫描将@RequestMapping注解标注的方法存入集合中)存入集合中的方法,是否有有我们现在所发出的请求地址名,如果有则进行下一步视图解析操作,如果无,则说明该请求无效。

  • DispatcherServlet类中的doGet方法:
    	@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          //1 获取请求URIString requestURI = req.getRequestURI();System.out.println(requestURI);//2 获取请求URI名称(从最后一个/处,加1截取)String requestURIName = requestURI.substring(requestURI.lastIndexOf("/") + 1);System.out.println(requestURIName);/*** 获取到请求名后,与我们实现存在map集合中的键值对比,是否存在*/if(mappings.containsKey(requestURIName )){
          //获取请求URI名称---在控制类中的所对应的方法的路径String classPath = mappings.get(requestURIName);System.out.println(classPath);//获取方法所在类的路径String className = classPath.substring(0,classPath.lastIndexOf("." ));System.out.println(className);//获取所对应的方法名(从最后一个“.”加1截取)String methodName = classPath.substring(classPath.lastIndexOf(".") + 1);System.out.println(methodName);try {
          //通过反射加载到类Class clz = Class.forName(className);//创建加载类对象Object obj = clz.getDeclaredConstructor().newInstance();//获取对象中的指定请求方法Method method = clz.getDeclaredMethod(methodName,null);//执行反射中对应的请求方法,并获取返回值(也即是页面名称)Object page = method.invoke(obj,null);System.out.println(page);req.getRequestDispatcher(page + ".jsp").forward(req,resp);} catch (Exception e) {
          e.printStackTrace();}}else {
          throw new ServletException("请求的URI不存在!");}}
    
    在这里插入图片描述

注意上述请求转发request.getRequestDispatcher(page + “.jsp”).forward(req,resp);
一定要交.jsp 后缀名,因为我们在web.xml配置文件中,配置的是< url-pattern>/< /url-pattern>,不会拦截.jsp文件,其余请求以及静态资源都会被当做请求拦截。

我们重启项目,在地址栏输入toLogin
在这里插入图片描述
查看控制台:
在这里插入图片描述
同理再地址栏输入toRegister和toDelete:
在这里插入图片描述
在这里插入图片描述

我觉类通过上述简单的详解,多少有点懂了吧,Spring MVC强不强,你我说了都不算,就像苏大强差不多,没有蔡根花小宝贝,苏大强是真的强!!!妥了~

  相关解决方案