一、 SpringMVC简介

springmvc是专门做web开发的spring框架,是在spring3.0后发布的。

1.1SpringMVC的优点

1.基于 MVC 架构

基于 MVC 架构,功能分工明确。解耦合,

M:model 模型 ->Dao,业务逻辑,负责处理业务逻辑,进行数据管理和数据库设计。

V:view 视图 ->表现层,负责前端页面的显示,与用户的交互

C:Controler 控制器->接收用户的请求,将请求跳转到模型处理,模型处理完毕后,在通过控制器,返回视图中的请求。

Spring MVC的特点 springmvc的特点和优势_mvc


2.SpringMVC是轻量级容器,jar包很小,不依赖特定的接口和类,符合依赖倒置原则

3.作为Spring框架的一部分,能够使用Spring的IoC和Aop,方 便 整 合Strtus,MyBatis,Hiberate,JPA 等其他框架。

4.4.SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解。方便灵活。

使用@Controller 创建处理器对象,@Service 创建业务对象,@Autowired 或者@Resource

在控制器类中注入 Service, Service 类中注入 Dao。

1.2第一个SpringMVC项目

作者使用的Tomcat9,java1.8

1.新建一个空的工程

2.新建一个mavenweb模块

创建后注意等待导入maven插件,如果不等待,暂时不会出现mavenweb的文件夹等。

Spring MVC的特点 springmvc的特点和优势_xml_02


这个过程第一个很漫长,建议在maven的pom.xml文件中加入阿里云仓库加快导入速度。

<mirror>
      <id>aliyunmaven</id>
      <mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
      <name>阿里云公共仓库</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>

mavenweb插件导入后的目录格式如下

Spring MVC的特点 springmvc的特点和优势_Spring MVC的特点_03


创建项目可能出现的问题:

a.这里的webapp和ch002-springmvc没有小蓝点。

有问题,我们查看fFle下的project-structure的Modules,果然,Modules没有ch002-springmvc,我们点击加号导入项目,注意要以maven的形式导入,这样,他可以自动识别webapp。

b.只是webapp没有蓝点,只需要在Modules模块的该项目ch002-springmvc下添加Web,并更改Web名称为webapp,修改Web Resource Directory的位置和web.xml的位置。

回到目录右键点击Reload from disk,刷新目录。

3、修改web.xml文件初始信息

因为web.xml文件刚开始的版本锅底,会导致后期前端无法识别jsp语句。

故需要修改web.xml

低版本

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

点击File-Project Sturcture -Module右边第一个Type框的Web Module Deployment Descriptor,删除,重加一下形式:

Spring MVC的特点 springmvc的特点和优势_xml_04


点击应用Apply,查看1web.xml为高版本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/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>

4、在pom.xml文件中添加依赖

spring-webmvc依赖,间接把spring的依赖都加入到项目
jsp,servlet依赖,junit依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>ch002-springmvc</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>ch002-springmvc Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>ch002-springmvc</finalName>
  </build>
</project>

注意这里的依赖导入和Tomcat版本不一致问题。如果你是Tomcat10,我建议你的版本最好是降低为Tomcat9,因为Tomcat10的servlet依赖是jarkata.servlet且用到的spring-webmvc也是高版本的。作者之前用的是Tomcat10+java17,导入的依赖不合适会导致后续很多问题,建议大家先用Tomcat9,或者寻找Tomcat10对应的依赖包版本。

5、在web.xml中注册springmvc框架的核心对象DispatcherServlet

1)DispatcherServlet叫做中央调度器, 是一个servlet, 它的父类是继承HttpServlet
2)DispatcherServlet页叫做前端控制器(front controller)
3)DispatcherServlet负责接收用户提交的请求, 调用其它的控制器对象,并把请求的处理结果显示给用户

<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.xml</param-value>
        </init-param>
         <!--在tomcat启动后,创建Servlet对象
            load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
                            tomcat创建对象的时间越早。 大于等于0的整数。
        -->

        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

a.注册springmvc的核心对象DispatcherServlet,需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
为什么要创建DispatcherServlet对象的实例呢?

因为DispatcherServlet在他的创建过程中,会同时创建springmvc容器对象,读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了。

b.注册DispactherServlet对象->执行init()方法->读取springmvc配置文件

打开 DispatcherServlet 的源码,其继承自 FrameworkServlet,而该类中有一个属性contextConfigLocation,用于设置 SpringMVC 配置文件的路径及文件名。该初始化参数的属性就来自于这里。

Spring MVC的特点 springmvc的特点和优势_Spring MVC的特点_05


url路径是指用户的请求是怎样的会走中央调度器。

对于,可以写为 / ,建议写为*.do 的形式。

配置文件可能出现的问题:
启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
Could not open ServletContext resource [/WEB-INF/myweb-servlet.xml]
springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/-servlet.xml .
一般情况下,配置文件是放在类路径下,即 resources 目录下。我们需要指定配置文件的路径。
注意这里标签内的name标签内部不能写其他的值,必须写contextConfigLocation

<init-param>
	<param-name>contextConfigLocation<param-name>
	<param-value>classpath:springmvc.xml<param-value>
<init-param>

6.创建springmvc.xml

在工程的类路径即 src 目录下创建 SpringMVC 的配置文件 springmvc.xml。该文件名可以任意命名。
因为要使用@Controller来管理springmvc对象,也就是司令官,我们需要在司令官这个类中调配。
故用注解@Controller,需要组件扫描器

<!--声明组件扫描器-->
    <context:component-scan base-package="com.link.controller" />

7、定义首页,在首页里面点击超链接发起请求

8、编写控制器类,也就是处理器

在类上与方法上添加相应注解即可。
@Controller:表示当前类为处理器
@RequestMapping:表示当前方法为处理器方法。该方法要对 value 属性所指定的 URI进行处理与响应。被注解的方法的方法名可以随意。

/**
 *  @Controller:创建处理器对象,对象放在springmvc容器中。
 *  位置:在类的上面
 *  和Spring中讲的@Service ,@Component
 *
 *  能处理请求的都是控制器(处理器): MyController能处理请求,
 *                         叫做后端控制器(back controller)
 *
 *  没有注解之前,需要实现各种不同的接口才能做控制器使用
 */
@Controller
public class MyController {
    /*
       处理用户提交的请求,springmvc中是使用方法来处理的。
       方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义
     */

    /**
     * 准备使用doSome方法处理some.do请求。
     * @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
     *                  一个请求指定一个方法处理。
     *       属性: 1. value 是一个String,表示请求的uri地址的(some.do)。
     *                value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”,代表是根路径。
     *       位置:1.在方法的上面,常用的。
     *            2.在类的上面
     *  说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
     *  使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
     *
     *  返回值:ModelAndView 表示本次请求的处理结果
     *   Model: 数据,请求处理完成后,要显示给用户的数据
     *   View: 视图, 比如jsp等等。
     */
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome()
    {
        // doGet()--service请求处理
        //处理some.do请求了。 相当于service调用处理完成了。  以后讲
        ModelAndView mv  = new ModelAndView();
        //添加数据, 框架在请求的最后把数据放入到request作用域。
        //request.setAttribute("msg","欢迎使用springmvc做web开发");
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        //指定视图, 指定视图的完整路径
        //框架在最后对视图执行的forward操作, request.getRequestDispather("/show.jsp).forward(...)
        //mv.setViewName("/show.jsp");
        mv.setViewName("/WEB-INF/show.jsp");
        return mv;
    }

若有多个请求路径均可匹配该处理器方法的执行,则@RequestMapping 的 value 属性中可以写上一个数组,RequestMapping注解还有一个属性Method,Method 属性的取值为 RequestMethod 枚举常量。

如果该请求路径下Method的读取请求的方式和用户发起请求的方式不同会出现405的错误。也就是前端与后端的请求方式不一致导致的。method=RequestMethod.GET

Spring MVC的特点 springmvc的特点和优势_mvc_06


@RequestMapping可以放到类上,用于该类上请求路径的公共路径。

ModelAndView 类中的 addObject()方法用于向其 Model 中添加数据。Model 的底层为一个 HashMap。

Model 中的数据存储在 request 作用域中,SringMVC 默认采用转发的方式跳转到视图,本次请求结束,模型中的数据被销毁。

9、定义目标页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
            <h3>show.jsp从request作用域获取数据</h3>
            <h3>msg数据:${msg}</h3><br/>
            <h3>fun数据:${fun}</h3>
</body>
</html>

二、Springmvc请求的处理流程

用户发送一个请求,会将请求传递给Tomcat(web.xml – url-pattern知道 *.do请求给DispatcherServlet来调配)------DispatcherServlet(DispatcherServlet读取配置文件找到组件扫描器,知道some.do请求路径对鸭doSome方法)----DispactherServlet把some.do转发给MyController.doSome方法—框架执行doSome()把得到的ModelAndView进行处理,转发到show.jsp
简化:some.do->DispatcherServlet–MyController

Spring MVC的特点 springmvc的特点和优势_spring_07

2.1中央调度器:

1、负责创建springmvc容器对象,读取xml配置文件,创建文件中的Controller对象

2、负责接收用户的请求,分派给自定义的Controller对象

Spring MVC的特点 springmvc的特点和优势_Spring MVC的特点_08

2.2 Springmvc执行过程源代码分析

1、启动过程

根据<load-on-startup>指定的1可知是在启动tomcat服务器的时候,就创建了中央调度器的实例,DispactherServlet是一个Servlet,在创建的过程中会执行init方法

//创建容器,读取类路径下的配置文件
WebApplicationContext ctx = new ClassPathXMLApplication("spring.xml");
//把容器对象放入ServletContext全局作用域中,map集合("容器id",容器对象)
getServletContext.setAttribute(key.ctx);

2、请求的处理过程

由Servlet来处理
执行Servlet的service方法–>doService(HttpServletRequest request,HttpServletResponse response)->doDisapatch(HttpServletRequest request,HttpServletResponse response)方法–>.handle()走处理器方法

执行流程的煎蛋分析:
(1)浏览器提交请求到中央调度器
(2)中央调度器直接将请求转给处理器映射器。
(3)处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
(4)中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
(5)处理器适配器调用执行处理器。
(6)处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
(7)处理器适配器直接将结果返回给中央调度器。
(8)中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。
(9)视图解析器将封装了的视图对象返回给中央调度器
(10)中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
(11)中央调度器响应浏览器。

2.3 解决问题:可以直接在浏览器上输入目标地址进入目标页面

如果目标页面在/WEB-INF/目录下,就无法进行访问。
如果/WEB-INF目录下有多个.jsp,我们在控制类的setViewName中会写太多重复代码,为了使代码得到复用,我们可以采用配置视图解析器。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view" />
        <property name="suffix" value=".jsp"/>
    </bean>

2.4 前端页面写,响应中文乱码问题,过滤器

之前在servlet中学过,用过滤器

现在在springmvc中同样用过滤器 这里采用的是CharacterEncodingFilter

源码分析

Spring MVC的特点 springmvc的特点和优势_xml_09

在web.xml文件中配置

<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>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注意这里的<url-pattern>标签内

  • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
  • /* 匹配所有路径。
  • *.do 后缀匹配。不要以 / 开始
  • /dept/* 前缀匹配。

Filter的生命周期?

  • 和Servlet对象生命周期一致。
  • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。

三、处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
➢ 请求中所携带的请求参数

当用户发来的请求携带参数时,服务器需要接收请求参数(数据)。

要求:处理器方法的形参名和请求中参数名必须一致。

3.1、逐个接收参数

底层实现:
1、框架使用request对象接收请求参数,从request域中取数据赋值给处理器方法形参。

String name = request.getParameter("name");  
String name = request.getParameter("name");

2、springmvc框架通过DispatcherServlet(的doDispatcher(request,response)方法内部的.handle方法)调用MyController的doSome()方法。
调用方法时,按名称对应,把接收的参数赋值给形参

doSome(name,Integer.valueOf(age))

框架会自动类型转换,无需我们程序员手动转换。

需要注意的是如果我们写的处理方法的age参数的类型为int,age在前端页面不写,也就是age的值为null,提交数据会发生400的错误。
400状态码是客户端错误,表示提交请求参数过程中,发生了问题。
包装类可以赋值为null


代码:

@RequestMapping(value = "/receiveproperty.do")
    public ModelAndView doSome(String name, Integer age){
        System.out.println("doSome, name="+name+"   age="+age);
        //可以在方法中直接使用 name , age
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",name);
        mv.addObject("myage",Integer.valueOf(age));
        //show是视图文件的逻辑名称(文件名称)
        mv.setViewName("show");
        return mv;
    }

3.2、对象为接收参数

注意点
1、定义的对象的属性名和请求参数名是一样的
底层实现:框架会自动创建java对象,调用set方法给属性赋值。
然后传值给这个请求参数Student.。

public ModelAndView doSome3(Student student){
        System.out.println("doSome, name="+student.getName()+"   age="+student.getAge());
        //可以在方法中直接使用 name , age
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("xmyname",student.getName());
        mv.addObject("xmyage",student.getAge());
        mv.addObject("mystudent",student.toString());
        //show是视图文件的逻辑名称(文件名称)
        mv.setViewName("show");
        return mv;
    }

校正请求参数名@RequestParam

请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,强行赋值会出现赋值为NULL的情况。
解决方法

@RequestParam("请求参数名")
public ModelAndView doSome2(@RequestParam(value = "rname",required = false) String name,
                               @RequestParam(value = "rage",required = false) Integer age)

注意这个只能给逐个赋值的请求参数使用,请求参数是对象的无法使用。