SpringMVC 是一种基于 Java 实现的 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring 的 Spring MVC 框架或集成其他 MVC 开发框架。
SpringMVC 它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。
创建一个 Maven 工程
选择骨架 org.apache.maven.archetypes:maven-archetype-webapp,点击 Next
添加 Maven Property(这样可以解决 maven 项目创建过慢的问题),点击 Next
Name:archetypeCatalog
Value:internal
选择项目本地路径,点击 Finish
补全 maven 项目目录结构(此时的 maven 项目目录结构还不完整)
导入坐标依赖
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<spring.version>5.0.2.RELEASEspring.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.0version>
<scope>providedscope>
dependency>
dependencies>
配置核心控制器
在 web.xml 配置文件配置
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
编写 springmvc.xml 的配置文件
在 resources 中右键 new --> File,命名为 springmvc.xml
<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">
beans>
配置一个 tomcat 服务器
在 webapp 包中创建 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
入门案例
入门程序
在 java 包中创建 com.zt.controller 包,创建 HelloController
/**
* 控制器
*/
@Controller
public class HelloController {
@RequestMapping(path="/hello")
public String sayHello(){
System.out.println("hello");
return "success";
}
}
在 WEB-INF 包中创建 pages 包,创建 success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
访问成功
配置 web.xml
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
配置 springmvc.xml
<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.zt">context:component-scan>
<bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/">property>
<property name="suffix" value=".jsp">property>
bean>
<mvc:annotation-driven>mvc:annotation-driven>
beans>
启动 tomcat 服务器
打开浏览器,输入以下地址访问
http://localhost/index.jsp
点击 “入门程序” 链接,若页面显示 “访问成功”,控制台打印 “hello",则案例运行成功
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。我们最常用的视图就是 jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
使 用
自动加载 RequestMappingHandlerMapping(处理映射器)和 RequestMappingHandlerAdapter(处理适配器 ) ,可 用 在 SpringMVC.xml 配 置 文 件 中 使 用
替代注解处理器和适配器的配置
RequestMapping 的作用
用于建立请求 URL 和处理请求方法之间的对应关系
RequestMapping 的出现位置
RequestMapping 可以作用于类和方法上:
RequestMapping 的属性
SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
基本数据类型和 String 类型
POJO 类型(包括实体类,以及关联的实体类)
数组和集合数据类型(List、map集合等)
SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。要求如下:
基本数据类型和字符串类型
要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
POJO 类型参数
要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。
数组和集合类型
要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。
给 List 集合中的元素赋值,使用下标。
给 Map 集合中的元素赋值,使用键值对。
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
请求参数的绑定
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/testParam")
public String testParam(String username){
System.out.println(username);
return "success";
}
}
Account
package com.zt.domain;
import java.io.Serializable;
public class Account implements Serializable {
private String username;
private String password;
private String money;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMoney() {
return money;
}
public void setMoney(String money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", money='" + money + '\'' +
'}';
}
}
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println(account);
return "success";
}
}
User
public class User implements Serializable {
private String uname;
private Integer age;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", age=" + age +
'}';
}
}
Account
public class Account implements Serializable {
private String username;
private String password;
private String money;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMoney() {
return money;
}
public void setMoney(String money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", money='" + money + '\'' +
", user=" + user +
'}';
}
}
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println(account);
return "success";
}
}
User
public class User implements Serializable {
private String uname;
private Integer age;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", age=" + age +
'}';
}
}
Account
public class Account implements Serializable {
private String username;
private String password;
private String money;
private List<User> userList;
private Map<String,User> userMap;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public Map<String, User> getUserMap() {
return userMap;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMoney() {
return money;
}
public void setMoney(String money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", money='" + money + '\'' +
", userList=" + userList +
", userMap=" + userMap +
'}';
}
}
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println(account);
return "success";
}
}
问题
post 请求方式的参数变成了乱码
解决
在 web.xml 中配置一个过滤器
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
User
public class User implements Serializable {
private String uname;
private Integer age;
private Date date;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", age=" + age +
", date=" + date +
'}';
}
}
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveUser")
public String saveUser(User user){
System.out.println(user);
return "success";
}
}
出现问题:此时我们如果在出生日期输入 2000-1-1,点击提交就会报错,原因是 String 类型的 2000-1-1,无法自动转换为 Date 类型,此时我们就需要自定义类型转换了
定义一个类,实现 Converter 接口
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-HH-dd");
if (source == null) {
throw new RuntimeException("输入日期为空");
} else {
try {
return simpleDateFormat.parse(source);
} catch (Exception e){
throw new RuntimeException("输入日期有误");
}
}
}
}
在 springmvc.xml 中配置类型转换器
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<array>
<bean class="com.zt.utils.StringToDateConverter">bean>
array>
property>
bean>
在 annotation-driven 标签中引用配置的类型转换服务
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean">mvc:annotation-driven>
SpringMVC 支持使用原始 ServletAPI 对象作为控制器方法的参数。
如下面使用 HttpServletRequest 对象:
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
获取 Servlet 原生的 API
ParamController
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request, HttpServletResponse response){
System.out.println(request.getCookies());
return "success";
}
}
作用
把请求中指定名称的参数给控制器中的形参赋值
属性
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
RequestParam实例
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(name = "uname") String username){
System.out.println(username);
return "success";
}
}
作用
用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
注意
get 请求方式不适用,因为 get 没有请求头
属性
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值
为 false,get 请求得到是 null。
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body){
System.out.println(body);
return "success";
}
}
打印结果
username=abc&age=20
作用
用于绑定 url 中的占位符。
例如:请求 url 中 /delete/{id},这个 {id} 就是 url 占位符。
url 支持占位符是 springmvc 支持 rest 风格 URL 的一个重要标志。
属性
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
PathVaribale实例
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testPathVaribale/{uid}")
public String testPathVaribale(@PathVariable(name = "uid") String id){
System.out.println(id);
return "success";
}
}
作用
用于获取请求消息头。
属性
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
RequestHeader实例
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader(name = "Accept") String head){
System.out.println(head);
return "success";
}
}
打印结果
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
作用
用于把指定 cookie 名称的值传入控制器方法参数。
属性
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
CookieValue实例
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue(name = "JSESSIONID") String cookieValue){
System.out.println(cookieValue);
return "success";
}
}
打印结果
D9FEFA4C3C3AB5CD8588FA6FED7FB9AC
作用
该注解可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。
属性
value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
应用场景
当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
实例一(基于 POJO 的使用)
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("testModelAttribute...");
System.out.println(user);
return "success";
}
@ModelAttribute
public void testShowUser(User user){
System.out.println("testShowUser...");
System.out.println(user);
}
}
打印结果
testShowUser...
User{uname='123', age=20, date=null}
testModelAttribute...
User{uname='123', age=20, date=null}
实例二(基于 Map 的应用场景:ModelAttribute 修饰方法带返回值)
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("testModelAttribute...");
System.out.println(user);
return "success";
}
@ModelAttribute
public User testShowUser(String uname){
// 模拟查询用户
User user = new User();
user.setUname(uname);
user.setAge(18);
user.setDate(new Date());
System.out.println("testShowUser...");
System.out.println(user);
return user;
}
}
打印结果
testShowUser...
User{uname='123', age=18, date=Tue Oct 08 15:13:46 CST 2019}
testModelAttribute...
User{uname='123', age=20, date=Tue Oct 08 15:13:46 CST 2019}
分析:我们没有提交日期字段,但使用了数据库对象原有的数据
实例三(基于 Map 的应用场景:ModelAttribute 修饰方法不带返回值)
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
AnnoController
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("1") User user){
System.out.println("testModelAttribute...");
System.out.println(user);
return "success";
}
@ModelAttribute
public void testShowUser(String uname, Map<String,User> map){
// 模拟查询用户
User user = new User();
user.setUname(uname);
user.setAge(18);
user.setDate(new Date());
System.out.println("testShowUser...");
System.out.println(user);
map.put("1",user);
}
}
打印结果
testShowUser...
User{uname='123', age=18, date=Tue Oct 08 15:21:08 CST 2019}
testModelAttribute...
User{uname='123', age=20, date=Tue Oct 08 15:21:08 CST 2019}
分析:我们没有提交日期字段,但使用了数据库对象原有的数据
作用
用于多次执行控制器方法间的参数共享
属性
实例
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
testSetSessionAttribute
testGetSessionAttribute
testCleanSessionAttribute
AnnoController
@Controller
@RequestMapping("/anno")
@SessionAttributes(value = "msg")
public class AnnoController {
@RequestMapping("/testSetSessionAttribute")
public String testSetSessionAttribute(Model model){
model.addAttribute("msg","123");
return "success";
}
@RequestMapping("/testGetSessionAttribute")
public String testGetSessionAttribute(ModelMap model){
System.out.println(model.get("msg"));
return "success";
}
@RequestMapping("/testCleanSessionAttribute")
public String testCleanSessionAttribute(SessionStatus sessionStatus){
sessionStatus.setComplete();
return "success";
}
}