Spring MVC学习笔记

视频:黑马 学习时间:2021年3月14日

1 三层架构

1.1 介绍

服务器端分成三层架构:

image-20211215195132179

1.2 MVC模型

MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。

V即View视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。

M即Model模型是指模型,表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。

C即Controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

image-20211215195140962

1.3 SpringMVC概述

SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用Spring 的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。

SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。

image-20211215195150570

2 SpringMVC入门程序

2.1 需求分析

image-20211215195159663

  1. 搭建开发环境

  2. 编写入门程序

2.2 搭建环境

导入坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- 版本锁定 --> 
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

前端拦截器

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

配置Tomcat服务器

image-20211215195209617

image-20211215195217672

2.3 代码编写

项目结构

image-20211215195237123

前端代码

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门程序</h3>

<a href="/hello">入门程序</a>

</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门成功</h3>
</body>
</html>

后端代码

HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 控制器类
*/
@Controller
public class HelloController {

// path:sayHello()的请求路径
@RequestMapping(path = "/hello")
public String sayHello(){
System.out.println("Hello SpringMVC!");
return "success";// 默认返回到success.jsp页面上
}
}

配置文件

SpringMVC框架的配置文件springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!--配置要扫描注解的包-->
<context:component-scan base-package="com.hongyi"></context:component-scan>

<!--配置视图解析器对象-->
<!--class:固定;id自取-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--prefix:视图资源所在目录-->
<!--suffix:视图的后缀名-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven></mvc:annotation-driven>

</beans>

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--只要服务器启动就加载-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--拦截所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

2.4 入门程序流程分析

  1. 启动服务器,加载一些配置文件
  • DispatcherServlet对象
  • springmvc.xml被加载了
  • HelloControllerInternalResourceViewResolver被spring创建成单例对象,被Spring的ioc容器管理
  1. 发送请求,后台处理请求

流程图如下:

image-20211215195248315

  1. 总结:
  • 服务器启动,应用被加载。读取到 web.xml 中的配置创建 spring 容器并且初始化容器中的对象。
  • 浏览器发送请求,被 DispatherServlet 捕获,该 Servlet 并不处理请求,而是把请求转发出去。转发的路径是根据请求 URL,匹配@RequestMapping 中的内容。
  • 匹配到了后,执行对应方法。该方法有一个返回值。
  • 根据方法的返回值,借助 nternalResourceViewResolver 找到对应的结果视图。
  • 渲染结果视图,响应浏览器。

2.5 涉及的组件

image-20211215195255878

DispatcherServlet:前端控制器

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

HandlerMapping:处理器映射器

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

Handler:处理器

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

HandlAdapter:处理器适配器

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

View Resolver:视图解析器

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

View:视图

SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

<mvc:annotation-driven>说明

在 SpringMVC 的各个组件中,==处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件==。使 用 <mvc:annotation-driven>自动加载 RequestMappingHandlerMapping (处理映射器) 和RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解处理器和适配器的配置。

它就相当于在 xml 中配置了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- 上面的标签相当于 如下配置-->
<!-- Begin -->
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>

<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- HandlerAdapter -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA
dapter">
</bean>
<bean
class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
<bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- HadnlerExceptionResolvers -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept
ionResolver"></bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolv
er"></bean>
<bean
class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"
></bean>
<!-- End -->

2.6 RequestMapping注解

源码

1
2
3
4
5
6
7
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
//...
}

作用

用于建立请求 URL 和处理请求方法之间的对应关系。

出现位置

  • 类上:请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。它出现的目的是为了使我们的 URL 可以按照模块化管理:例如:
1
2
3
4
5
6
7
8
9
账户模块:
/account/add
/account/update
/account/delete
...
订单模块:
/order/add
/order/update
/order/delete

前面的部分就是把RequsetMappding 写在类上,使我们的 URL 更加精细。

  • 方法上:请求 URL 的第二级访问目录。

  • 代码演示:

1
<a href="/user/testRequestMapping">RequestMapping注解</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Controller
@RequestMapping(path = "/user")// 注解在类上,表示请求url的第一部分的地址
public class HelloController {

// path:sayHello()的请求路径
@RequestMapping(path = "/hello")
public String sayHello(){
System.out.println("Hello SpringMVC!");
return "success";// 默认返回到success.jsp页面上
}

/**
* RequestMapping注解
* @return
*/
@RequestMapping(path = "/testRequestMapping")// 实际对应请求地址为/user/testRequestMapping
public String testRequestMapping(){
System.out.println("测试RequestMapping注解...");
return "success";
}
}

RequestMapping注解的属性

  • value:用于指定请求的 URL。它和 path 属性的作用是一样的。当只有一个value或path属性时可以省略,否则不能省略。
1
@RequestMapping("/hello")
  • method:用于指定请求的方式。
1
@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})// RequestMethod是一个枚举类
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和配置的一模一样。例如:
1
2
params = {"accountName"}// 表示请求参数必须有 accountName
params = {"moeny!100"}// 表示请求参数中 money 不能是 100。
  • headers:用于指定限制请求消息头的条件。

注意:以上4个属性只要出现 2 个或以上时,他们的关系是与的关系。

3 请求参数的绑定

3.1 绑定机制

表单中请求参数都是基于 key=value 键值对的。SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。例如:

1
2
<!--请求参数是:accountId=10-->
<a href="account/findAccount?accountId=10">查询账户</a>
1
2
3
4
5
6
7
8
9
/**
* 查询账户
* @return
*/
@RequestMapping("/findAccount")
public String findAccount(Integer accountId) {
System.out.println("查询了账户。。。。"+accountId);
return "success";
}

3.2 支持的数据类型

  • 基本类型参数:包括基本类型和 String 类型

  • POJO类型参数:包括实体类,以及关联的实体类

  • 数组和集合类型参数:包括 List 结构和 Map 结构的集合(包括数组)

说明:SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。

3.3 基本类型和String类型作为参数

  • 使用要求:要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)

代码演示

jsp代码:

1
<a href="param/testParam?username=hehe&password=123456">请求参数绑定</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 请求参数绑定
*/
@Controller
@RequestMapping("/param")
public class ParamController {
/**
* 请求参数绑定的入门案例
* @return
*/
@RequestMapping("/testParam")
public String testParam(String username,String password){
System.out.println("执行了...");
System.out.println("你的用户名是:"+username);
System.out.println("你的密码是:"+password);
return "success";
}
}

执行结果:

image-20211215195309666

3.4 POJO类型作为参数

3.4.1 要求

要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。

代码演示

Account.java

1
2
3
4
5
6
7
8
public class Account implements Serializable {
private String username;
private String password;
private double money;
// 引用类型的属性
private User user;
// 省略了set/get方法
}

User.java

1
2
3
4
5
public class User implements Serializable {
private String uname;
private Integer age;
// 省略了set/get方法
}

jsp代码

1
2
3
4
5
6
7
8
<form action="/param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>
用户姓名:<input type="text" name="user.uname"/><br/>
用户年龄:<input type="text" name="user.age"/><br/>
<input type="submit" value="提交"/>
</form>

控制层代码

1
2
3
4
5
6
7
8
9
10
/**
* 请求参数绑定把数据封装到javabean的类中
* @return
*/
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println("执行了...");
System.out.println(account);
return "success";
}

执行结果

image-20211215195317905

3.4.2 配置解决中文乱码的过滤器

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--配置解决中文乱码的过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

3.5 集合类型作为参数

要求

  • 第一种:要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。给 List 集合中的元素赋值,使用下标。给 Map 集合中的元素赋值,使用键值对。

  • 第二种:接收的请求参数是 json 格式数据。需要借助一个注解实现。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--类中存在list和map的集合-->
<form action="/param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>

用户姓名:<input type="text" name="list[0].uname"/><br/>
用户年龄:<input type="text" name="list[0].age"/><br/>

用户姓名:<input type="text" name="map['one'].uname"/><br/>
用户年龄:<input type="text" name="map['one'].age"/><br/>
<input type="submit" value="提交"/>
</form>

Account类属性:

1
2
3
4
5
6
private String username;
private String password;
private double money;
// 集合属性
private List<User> list;
private Map<String,User> map;

执行结果:

image-20211215195325747

3.6 自定义类型转换器

3.6.1 演示异常

1
2
3
4
5
6
7
<!--自定义类型转换器-->
<form action="/param/saveUser" method="post">
用户姓名:<input type="text" name="uname"/><br/>
用户年龄:<input type="text" name="age"/><br/>
用户生日:<input type="text" name="date"/><br/>
<input type="submit" value="提交"/>
</form>
1
2
3
4
5
public class User implements Serializable {
private String uname;
private Integer age;
private Date date;
}
1
2
3
4
5
6
7
8
9
10
11
/**
* 自定义类型转换器
* @param user
* @return
*/
@RequestMapping("/saveUser")
public String saveUser(User user){
System.out.println("执行了...");
System.out.println(user);
return "success";
}

正常情况:

image-20211215195347575

image-20211215195359638

异常情况:

image-20211215195408371

image-20211215195416343

3.6.2 转换器代码编写

字符串转日期的工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 把字符串转换成日期
*/
public class StringToDateConverter implements Converter<String, Date> {

/**
* @param s 传入进来的字符串
* @return
*/
@Override
public Date convert(String s) {
if(s == null){
throw new RuntimeException("请传入数据");
}
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try {
// 把字符串转换成日期
return df.parse(s);
} catch (Exception e) {
throw new RuntimeException("数据类型转换异常");
}
}
}

springmvc.xml配置

1
2
3
4
5
6
7
8
9
10
11
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.hongyi.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>

<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

执行结果

image-20211215195431508

3.7 获取Servlet原生的API

SpringMVC 还支持使用原始 ServletAPI对象作为控制器方法的参数。可以把上述对象,直接写在控制的方法参数中使用。

代码演示

1
<a href="param/testServlet">测试Servlet原生API</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 原生API的获取
* @return
*/
@RequestMapping("/testServlet")
public String testServlet(HttpServletRequest request, HttpServletResponse response){
System.out.println("执行了...");
System.out.println(request);

HttpSession session = request.getSession();
System.out.println(session);

ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);

System.out.println(response);
return "success";
}

执行结果

image-20211215195441557

4 常用注解