欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springmvc-介绍,重要组件以及执行流程

程序员文章站 2022-05-20 14:10:08
...

一、Web、MVC、WebMVC

  • Web:Web完成一次请求的过程
    1. Web浏览器发起请求
    2. Web服务器接受请求并处理,产生响应
    3. Web服务器完成处理后,返回内容给Web客户端,Web客户端接收响应的内容,自行处理并显示出来
  • 在Web中,都是Web客户端发起的请求,Web服务器接收处理请求并产生响应。
    • 一般Web服务器不能主动通知Web客户端更新内容。有一些技术可以从服务器端通知客户端进行更新内容:Comet服务器推技术,还有HTML5中的WebSocket技术
  • MVC设计模式(model view controller):是一种设计架构的模式,本生不引入新的功能,只是帮助我们进行功能的细致划分,优化代码结构。
    1. Model:数据模型。提供要展示的数据,包含数据的封装和操作数据的行为。现在一般将Value Object数据和行为分开。数据有实体类存储或者Java Bean来提供,行为有service层来处理。
    2. View:负责Model封装实体类数据的展示。
    3. Controller:接受到用户请求,委托给模型进行处理,处理完毕后把返回的模型数据交给视图。控制器起到一个调度作用。
    4. 在标准的MVC中,模型能够主动推送数据给视图进行跟新(采用观察者设计模式,在模型上注册视图,在模型更新时候自动跟新视图)。
  • 三层架构和WebMVC的区别:
    • 三层架构是对整个Web前后端项目进行功能的划分
    • MVC是在三层架构中Web层的细致化分,将Web层分为model(数据封装成的模型对象),view(将模型渲染到视图上展示),controller(和Service层进行数据交互,并且负责调度模型和视图)
  • WebMVC:Web中MVC里面的模型-视图-控制器的概念和标准MVC概念一样。
    • 在标准MVC中,服务器是可以主动推送数据给库和端的;
    • 在WebMVC中,服务器是无法实现主动推送数据给客户端的,一定要通过请求和响应,浏览器通过刷新发送请求,服务器再通过响应推送数据给客户端
    • Servlet作为Controller,Jsp作为View,Service层作为Model的行为,Java Bean作为Model对象

二、Spring MVC的工作原理

SpringMVC的重要组件

  • SpringWebMVC简称为Spring MVC:SpringMVC是Spring框架提供给我们简化Web项目开发的一个模块,通过SpringMVC很好的将数据、业务和显示进行分离。

  • SpringMVC和其他WebMVC框架一样,都是处理请求和响应。

  • SpringMVC中核心类与接口:

    1. DispacherServlet:前端控制器。用来过滤客户端请求

      • DispatcherServlet:前置(前端)控制器,可以看出间DispatcherServlet通过Spring的HttpServletBean继承了HttpServlet,所以DispatcherServlet相当于一个Servlet springmvc-介绍,重要组件以及执行流程
    2. HandlerMapping:处理器/映射器。将请求url按照一定的规则转发给对应的Handler适配器对象。

      1. HandlerMapping接口:关联请求url和映射路径

      2. HandlerMapping接口的实现类:

        1. SimpleUrlhandlerMapping:通过Spring配置文件设置映射器,关联控制器在容器中拦截路径与WebContent中Web.xml的<servlet-mapping)>的

        2. DefaultAnnotationHandlerMapping:通过注解,将访问的URl映射到Controller上

        springmvc-介绍,重要组件以及执行流程

    3. HandlerAdapter:适配器。处理适配器要处理的请求Handler对象,指定Controller控制器处理请求和响应

      1. HandlerAdapter接口:处理器/适配器,处理请求访问路径选择合适的控制器

      2. AnnotationMethodHandlerAdapter类:采用注解方式

        springmvc-介绍,重要组件以及执行流程

    4. Controller接口:控制器,用来处理请求和响应

      1. AbstractController是Controller接口的实现抽象类,对会话实现加锁(同步,避免多个请求同时访问控制器的问题)synchronizeOnSession;指定请求报文请求方法以及是否必须携带Session会话。

        //AbstractController抽象类的源码
        //多个请求访问Controller,AbstractController保证session多线程访问安全
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    return handleRequestInternal(request, response);
                }
            }
        }
        

      springmvc-介绍,重要组件以及执行流程

    5. HandlerInterceptor接口:拦截器,需要我们自己实现该接口

    6. ViewResolver接口:视图解析器。

      1. UrlBasedViewResovler类:通过spring配置文件,将控制器controller返回的逻辑视图交给ViewResolver进行处理,一般将数据通过EL标签渲染到Jsp页面

      2. InternalResourseViewResolver类,支持Jstl标签库的使用

        springmvc-介绍,重要组件以及执行流程

    7. View接口:视图。通过标签渲染到JSP页面上展示数据。

      1. JstlView类

        springmvc-介绍,重要组件以及执行流程

    8. LocalResolver接口

      springmvc-介绍,重要组件以及执行流程

    9. HandlerExceptionResolver接口 --异常处理

      1. SimpleMappingExceptionResolver实现类

        springmvc-介绍,重要组件以及执行流程

  • DispatcherServlet:前置控制器。配置在WebContext的Web.xml文件中。拦截匹配的请求url,Servlet拦截匹配的规则需要自己定义,把拦截请求,再根据我们自己定义的Controller来处理

    • 可以在Web.xml文件中配置多个DispatcherServlet
    • 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件/WEB-INF/[servletName]-servlet.xml, 生成配置文件中的bean
    web.xml
    第一个例子:查找默认的Spring配置文件
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    	version="3.1">
        <!-- 配置DispatcherServlet前端控制器:相当于之前的Servlet配置 -->
        <!-- 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件 -->
        <!-- /WEB-INF/[servletName]-servlet.xml -->
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-web-mvc.xml</param-value>
            </init-param>
            <!-- 启动顺序:让这个Servlet随容器启动而创建 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!-- 自定义拦截规则 -->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
           	<!-- *.do *.html传统的方式。不会导致静态资源被拦截 -->
            <url-pattern>*.action</url-pattern>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
        
    第二个例子:Servlet参数配置指定的Spring配置文件,多个Spring配置文件,<value>,</value>中用逗号分隔
     <servlet>  
        <servlet-name>springMVC</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-web-mvc.xml</param-value>
            <!--或者采用-->
            <!--<param-value>/WEB-INF/classes/springMVC.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>  
    
  • 父子上下文:WebApplicationContext,使用监听器会创建WebApplicationContext

    • Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

      • 可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);
    • DispatcherServlet是一个Servlet,可以同时配置多个Servlet,每个DispatcherServlet都有自己的上下文对象(WebApplicationContext),称为子上下文(子容器)

      • 子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是DispatcherServlet.class.getName() + “.CONTEXT”。
      • 可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);
    • 说明 :Spring 并没有限制我们,必须使用父子上下文。我们可以自己决定如何使用。

    • 一:

    1. 父上下文容器中保存数据源、服务层、DAO层、事务的Bean
    2. 子上下文容器中保存Mvc相关的Action的Bean.
    3. 事务控制在服务层。
    4. 由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。
    5. 当然,做为“传统型”方案,也没有必要这要做。
    • 二:

      1. Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。

      2. 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。

      3. 所以“激进型”方案就出现了-----没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

      4. 由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

    • 总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了。

  • SpringMVC框架在项目中的搭建:

    1. 完整的web.xml配置文件

      1. DispatcherServlet的配置
      2. 静态资源采用Tomcat中web.xml配置的默认的defaultServlet处理静态资源文件(解决DispatcherServlet拦截“/”所有的资源问题)
      3. 过滤器filter(设置请求和响应编码)
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
      version="3.1">
      
      <!-- 配置DispatcherServlet前端控制器:相当于之前的Servlet配置 -->
      <!-- 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件 -->
      <!-- /WEB-INF/[servletName]-servlet.xml -->
      <servlet>
          <servlet-name>SpringMVC</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!--配置Servlet参数,指定Spring配置文件,解析spring配置文件,生成bean-->
          <!-- 方法一:默认去WEB-INF下查找 -->
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <!-- 方法二:classpath指定相对路径,相对于src -->
              <param-value>classpath:spring-web-mvc.xml</param-value>
              <!-- 方法三:指定WEB-INF下的Spring配置文件 -->
              <!-- 二者选其一 -->
              <param-value>/WEB-INF/SpringMVC-servlet.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>SpringMVC</servlet-name>
          <!-- *.action防止拦截静态资源jsp,html,css,images -->
          <!-- <url-pattern>*.action</url-pattern> -->
          <!-- "/"拦截所有请求,而不是"/*" -->
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
      
      <!-- 解决静态资源无法访问问题 -->
      <!-- 第一种:使用利用Tomcat的web.xml配置文件中defaultServlet来处理静态文件 -->
      <servlet-mapping>
          <servlet-name>default</servlet-name>
          <url-pattern>*.css</url-pattern>
          <url-pattern>*.js</url-pattern>
          <url-pattern>*.png</url-pattern>
      </servlet-mapping>
      
      
      <!-- 过滤器 -->
      <filter>
          <filter-name>CharacterEncodingFilter</filter-name>
          <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>UTF-8</param-value>
          </init-param>
          <init-param>
              <param-name>forceEncoding</param-name>
              <param-value>true</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CharacterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
      </web-app>
      
    2. Spring配置文件xml:

      1. 注入映射器(默认映射器),适配器(默认适配器),视图解析器(默认视图解析器,但需要处理逻辑视图名,添加前后缀),控制器(指定自定义控制器,id后者name映射)对象
        • 对于Spring配置文件中的处理器适配器,处理器映射器,都可以省去不写,springMVC框架中会有默认配置的,视图解析器也可以不配置,因为在org.springframework.web.servlet.DispatcherServlet这个类的同包下,有一个DispatcherServlet.properties文件,里面就是SpringMVC默认的配置,是当用户的Spring配置文件中没有指定配置时使用的默认策略
      2. 解决静态资源拦截问题
        • 第二种:通过Spring的< mvc:resources>标签
        • 第三种: 使用< mvc:default-servlet-handler/>
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      	xmlns:mvc="http://www.springframework.org/schema/mvc"
      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xmlns:context="http://www.springframework.org/schema/context"
      	xsi:schemaLocation="
              http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/mvc
              http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
              http://www.springframework.org/schema/context 
      		http://www.springframework.org/schema/context/spring-context-4.3.xsd">
      
      	<!-- 解决静态资源无法访问问题 -->
      	<!-- 第二种:通过Spring的<mvc:resources>标签,例如: -->
      	<mvc:resources mapping="/static/ima/**" location="/static/images/" />
      	<mvc:resources mapping="/static/js/**" location="/static/js/" />
      	<mvc:resources mapping="/static/css/**" location="/static/css/" />
      	
      	<!-- 解决方式三: 使用<mvc:default-servlet-handler/>标签在spring配置文件中加入此标签配置即可 -->
      	<mvc:default-servlet-handler/>
      
      	<!-- 配置HandlerMapping映射器,有默认值 -->
      	<!-- 将url-pattern映射到对应的Controller控制器 -->
      	<bean
      		class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
      
      	<!-- 配置HandlerAdapter适配器 -->
      	<bean
      		class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
      
      	<!-- 配置ViewResolver视图解析器 -->
      	<bean
      		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <!-- 支持Jstl标签使用 -->
      		<property name="viewClass"
      			value="org.springframework.web.servlet.view.JstlView" />
      		<!-- 添加逻辑视图名的前缀 -->
      		<property name="prefix" value="/WEB-INF/jsp/" />
      		<!-- 添加逻辑视图名的后缀 -->
      		<property name="suffix" value=".jsp" />
      	</bean>
      
      	<!-- 配置controller控制器 -->
      	<bean name="/hello"
      		class="com.briup.controller.HelloController" />
      
      </beans>
      
      
      指定src下的Spring配置文件,采用默认的映射器和适配器
      <!-- 解决静态资源无法访问问题 -->
      <!-- 解决方式三: 使用<mvc:default-servlet-handler/>标签在spring配置文件中加入此标签配置即可 -->
      <mvc:default-servlet-handler />
      
      <!-- 配置ViewResolver视图解析器 -->
      <bean
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="viewClass"
              value="org.springframework.web.servlet.view.JstlView" />
          <!-- 添加逻辑视图名的前缀 -->
          <property name="prefix" value="/WEB-INF/jsp/" />
          <!-- 添加逻辑视图名的后缀 -->
          <property name="suffix" value=".jsp" />
      </bean>
      
      <!-- 配置controller控制器 -->
      <bean name="/hello.action"
          class="com.briup.controller.HelloController" />
      
      <bean name="/test" class="com.briup.controller.TestController">
         <!-- 多个客户端访问session会出现线程安全问题,对session进行加锁 -->
          <property name="synchronizeOnSession" value="true"></property>
          <!-- 设置可以接受的请求方法post,get -->
          <property name="supportedMethods" value="POST,GET"></property>
          <!-- requireSession,设置请求是否一定要携带session,没有session会抛出异常 -->
          <property name="requireSession" value="false"></property>
      </bean>
      
    3. 自定义Controller控制器

      //处理器,控制器
      //接收前端的请求,处理请求,返回model和view
      public class HelloController implements Controller {
      	@Override
      	public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
      		
      		//模型和视图
      		ModelAndView mv = new ModelAndView();
      		
      		//往模型对象中存放值
      		mv.addObject("name", "tom");
      		
      		//指定跳转的逻辑视图名
      		mv.setViewName("hello");
      		
      		return mv;
      	}
      }
      
      //研究Controller的实现类AbstractController,实现线程同步,请求访问方法
      public class TestController extends AbstractController {
      	@Override
      	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
      			throws Exception {
      		
      		//ModelAndView mv = new ModelAndView("hello");
      		//mv.addObject("name", "zs");
      		
      		PrintWriter writer = response.getWriter();
      		writer.write("hello world!");
      		writer.flush();
      
      		return null;
      	}
      }
      
    4. 展示页面jsp:放在WEB-INF下的页面,只能通过内部跳转的方式访问到,因为客户端访问不到WEB-INF目录,而且服务器端可以访问到WEB-INF目录

      • 需要引入jstl相关jar包
      • 页面中的路径问题 ,设置basePath
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      	pageEncoding="UTF-8"%>
      <%
      	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
      			+ request.getContextPath() + "/";
      %>
      
      <!DOCTYPE html>
      <html>
          <head>
              <meta charset="UTF-8">
              <title>hello.jsp</title>
              <base href="<%=basePath %>">
              <link href="static/css/hello.css" rel="stylesheet" type="text/css"/>
          </head>
          <body>
              <h1>hello,${name }</h1>
          </body>
      </html>
      
  • 整个访问流程:

    1. 首先用户发送请求,前置控制器DispatcherServlet收到请求后自己不进行处理,而是委托给其他解析器进行处理,前置控制器作为唯一的访问点,进行全局的流程控制。
    2. DispatcherServlet把请求转交给HandlerMapping,HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器)
    3. DispatcherServlet再把请求转交给HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器(适配器模式).简单点说就是让我们知道接下来应该调用Controller处理器里面的什么方法
    4. HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
    5. ModelAndView的逻辑视图名交给ViewResolver解析器, ViewResolver解析器把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术
    6. View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术
      ionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器)
    7. DispatcherServlet再把请求转交给HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器(适配器模式).简单点说就是让我们知道接下来应该调用Controller处理器里面的什么方法
    8. HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
    9. ModelAndView的逻辑视图名交给ViewResolver解析器, ViewResolver解析器把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术
    10. View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术
    11. 最后返回到DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。