一、Spring MVC初步认识
1.1介绍
Spring MVC是Spring Framework提供的Web组件,全称是Spring Web MVC,是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能
Java Web开发者必须要掌握的技术框架
1.2MVC是什么
MVC是一种软件架构思想,把软件按照模型,视图,控制器来划分
View:视图层,指工程中的html,jsp等页面,作用是和用户进行交互,展示数据
Controler:控制层,指工程中的Servlet,作用是接收请求和响应浏览器
Model:模型层,指工程中的JavaBean,用来处理数据
JavaBean分成两类:
- 一类称为实体类Bean:专门用来存储业务数据,比如Student,User
- 一类称为业务处理Bean:指Servlet或Dao对象,专门用来处理业务逻辑和数据访问
流程:
- 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收
- Controller调用相应的Model层处理请求,处理完毕后结果返回到Controller
- Controller再根据请求处理的结果找到对应的View视图,渲染数据后最终响应给浏览器
Spring MVC对这套MVC流程进行封装,帮助开发者屏蔽底层细节,并且开放出相关接口供开发者调用,让MVC开发更简单方便
1.3 核心组件
- DispatcherServlet:前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Spring MVC的核心模块
- Handler:处理器,完成具体的业务逻辑,相当于Servlet
- HandlerMapping:DispatcherServlet是通过 HandlerMapping把请求映射到不同的Handler
- HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成
- HandlerExecutionChain:处理器执行链,包括两部分内容:Handler和HandlerInterceptor(系统会有一个默认的HandlerInterceptor,如果有额外拦截处理,可以添加拦截器进行设置)
- HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型转换、把表单数据封装到POJO等,这些一系列的操作都是由
- HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler
- ModelAndView:封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet
- ViewResolver:视图解析器,DispatcherServlet通过它把逻辑视图解析为物理视图,最终把渲染的结果响应给客户端
1.4 工作流程
- 客户端请求被DispatcherServlet接收
- 根据HandlerMapping映射到Handler
- 生成Handler和HandlerInterceptor
- Handler和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet
DispatcherServlet通过HandlerAdapter调用Handler的方法完成业务逻辑处理 - 返回一个ModelAndView对象给DispatcherServlet
- DispatcherServlet把获取的ModelAndView对象传给ViewResolver视图解析器,把逻辑视图解析成物理视图
- ViewResolver返回一个View进行视图渲染(把模型填充到视图中)
- DispatcherServlet把渲染后的视图响应给客户端
二、Spring MVC环境搭建
2.1创建maven工程,修改pom.xml加入Spring MVC的依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SpringMVC</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringMVC Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.19</version>
</dependency>
</dependencies>
</project>
2.2在web.xml中配置Spring MVC的DispatcherServlet
- 首先在项目中创建java和resources的目录
- 在resources目录中添加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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
- 然后在web.xml 配置Spring MVC的DispatcherServlet
<!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>
<!-- 配置核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置文件加载路径
1)默认情况下,读取WEB-INF下面的文件
2)可以改为加载类路径下(resources目录),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
DispatcherServlet对象创建时间问题
1)默认情况下,第一次访问该Servlet的创建对象,意味着在这个时间才去加载springMVC.xml
2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
<load-on-startup>1</load-on-startup>
数值越大,对象创建优先级越低! (数值越低,越先创建)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<!--*.do拦截以do结尾的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
拦截请求其实就是说,只要是符合这个URL的请求都会进入到Spring MVC中去看看有没有对应的Handler./不会拦截.jsp的路径,但是会拦截.html等静态资源
- DispatcherServlet是Spring MVC提供的核心控制器,这个一个Servlet程序,该Servlet程序会接收所有请求
- 核心控制器会读取一个springmvc.xml配置,加载Spring MVC的核心配置
- 配置/代表拦截所有请求
- 代表在项目启动时实例化DispathcerServlet,如果没有配置,则在第一次访问Servlet时进行实例化
- 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: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/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置自动扫包 -->
<context:component-scan base-package="com.zyh.controller"></context:component-scan>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--给逻辑视图加上前缀和后缀 -->
<!--前缀-->
<property name="prefix" value="/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
- 创建Controller控制器Handler,在里面编写接收参数,调用业务方法,返回视图页面等逻辑
@Controller
public class HelloHandler {
/**
* 当客户端访问index请求时
* 直接自动关联到这个方法
* 执行这个方法后,会返回结果
* @return
*/
@RequestMapping("/index")
public String index(){
System.out.println("接收到了请求");
//返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图
//这里返回的只是页面的名称,不是完整的页面访问路径
return "index";
}
}
@Controller注解是为了让Spring IOC容器初始化时自动扫描到该Controller类;@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/;方法返回的结果是视图的名称index,该名称不是完整页面路径,最终会经过视图解析器解析为完整页面路径并跳转。
- 配置Tomcat
- 测试
三、@RequestMapping注解
Spring MVC通过@RequestMapping注解把URL请求和业务方法进行映射,在控制器的类定义处以及方法定义处都可以添加@RequestMapping,在类定义处添加相当于多了一层访问路径
@RequestMapping常用参数:
- value:指定URL请求的实际地址,是@RequestMapping的默认值
- method:指定请求的method类型,包括GET、POST、PUT、DELETE等
- params:指定request请求中必须包含的参数值,如果不包含的话,就无法调用该方法
@RequestMapping(value = "/index",method = RequestMethod.POST,params="id")
public String index(){
System.out.println("接收到了请求");
//返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图
//注意:这里返回的只是页面名称,不是完整的页面访问路径
return "index";
}
四、参数绑定
4.1 URL风格参数绑定
params是对URL请求参数进行限制,不满足条件的URL无法访问该方法,需要在业务方法中获取URL的参数值。
- 在业务方法定义时声明参数列表
- 给参数列表添加@RequestParam注解进行绑定
Spring MVC可以自动完成数据类型转换,该工作是由HandlerAdapter来完成的
4.2 RESTful风格的URL参数获取
- 传统的URL:localhost:8080/hello/index?id=1&name=tom
- RESTful URL:localhost:8080/hello/index/1/tom
@RequestMapping("/restful/{id}/{name}")
public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
System.out.println(num+"-"+name);
return "index";
}
4.3映射Cookie
@RequestMapping("/cookie")
public String getCookie(@CookieValue("JSESSIONID") String sessionId){
System.out.println(sessionId);
return "index";
}
4.4使用POJO绑定参数
Spring MVC会根据请求参数名和POJO属性名进行匹配,自动为该对象填充属性值,并且支持属性级联
首先创建实体类
为了方便测试,写一个addUser.jsp页面
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/hello/add" method="post">
<table>
<tr>
<td>编号:</td>
<td>
<input type="text" name="id">
</td>
</tr>
<tr>
<td>姓名:</td>
<td>
<input type="text" name="name">
</td>
</tr>
<tr>
<td>
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
然后在Handler中,编写相关方法
启动Tomcat服务器
结果发现出现乱码问题
为了解决这个问题,我们只需要在web.xml配置文件中配置过滤器就可以了
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.5JSP页面的转发和重定向
Spring MVC默认是通过转发的形式响应JSP,可以手动进行修改
@RequestMapping("/restful/{id}/{name}")
public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
System.out.println(num+"-"+name);
return "index";
}
重定向:
@RequestMapping("/restful/{id}/{name}")
public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
System.out.println(num+"-"+name);
return "redirect:/index.jsp";
}
设置重定向的时候不能写逻辑视图,必须写明资源的物理路径,比如"rediect:/index.jsp"
转发:
@RequestMapping("/restful/{id}/{name}")
public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
System.out.println(num+"-"+name);
return "forward:/index.jsp";
}
五、数据绑定
- 数据绑定:在后台业务方法中,直接获取前端HTTP请求中的参数
- HTTP请求传输的参数都是String类型的,Handler业务方法中的参数是开发者指定的参数类型,比如int,Object,所以需要进行数据类型的转换
- Spring MVC的HandlerAdapter组件会在执行Handler业务方法之前,完成参数的绑定,开发者直接使用即可
5.1基本数据类型
@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
return "id:"+id;
}
客户端HTTP请求中必须包含id参数,否则抛出500异常,因为id不能为null
同时id的值必须为数值,而且必须为整数,否则抛出400异常
5.2包装类
@RequestMapping("/packageType")
@ResponseBody
public String packageType(Integer id){
return "id:"+id;
}
如果HTPP请求中没有包含id参数,不会报错,id的值就是null,会直接返回id:null给客户端,但是如果id=a,或者id=1.2,同样会抛出404异常,因为数据类型无法转换
- value=“id”:把HTTP请求中名字为id的参数和Handler业务方法中的形参进行映射
- required:true表示id参数必须填,false表示非必填
- defaultValue=“0”:表示当HTTP请求中没有id参数的时候,形参的默认值是0
5.3数组类型
@RequestMapping("/arrayType")
@ResponseBody
public String arrayType(String[] names){
StringBuffer buffer = new StringBuffer();
for (String str:names){
buffer.append(str).append(" ");
}
return "names:"+buffer.toString();
}
5.4POJO(java对象)
public class User {
private Integer id;
private String name;
private Address address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Address {
private Integer code;
private String value;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "Address{" +
"code=" + code +
", value='" + value + '\'' +
'}';
}
}
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/hello/add" method="post">
<table>
<tr>
<td>编号:</td>
<td>
<input type="text" name="id">
</td>
</tr>
<tr>
<td>姓名:</td>
<td>
<input type="text" name="name">
</td>
</tr>
<tr>
<td>地址编号:</td>
<td>
<input type="text" name="address.code">
</td>
</tr>
<tr>
<td>地址信息:</td>
<td>
<input type="text" name="address.value">
</td>
</tr>
<tr>
<td>
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
我们可以在springmvc.xml中添加一个消息转换器把中文乱码解决掉
前后端转换的数据称为消息
解决响应时乱码问题,springmvc.xml中配置转换器即可
5.5 List
Spring MVC不支持List类型的直接转换,需要包装成Object
public class UserList {
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
}
注意:User类一定要有无参构造,否则抛出异常
@RequestMapping("/listType")
@ResponseBody
public String listType(UserList userList){
StringBuffer buffer = new StringBuffer();
for (User user:userList.getUserList()){
buffer.append(user);
}
return "用户:"+buffer.toString();
}
5.6 JSON
- JSON数据必须用JSON.stringfy()方法转换成字符串
- contentType:"application/json;charset=UTF-8"不能省略
- @RequestBody注解
读取HTTP请求参数,通过SpringMVC提供的HttpMessageConverter接口把读取的参数转换为JSON、XML格式的数据,绑定到业务方法的形参
需要使用组件结合@RequestBody注解把JSON转为JavaBean,这里使用FastJson,其优势是如果属性为空,就不会将其转为JSON
把业务方法返回的对象,通过HttpMessageConverter接口转为指定格式的数据,JSON、XML等,响应给客户端