SpringMVC框架
一、什么是三层架构和MVC模式?
在开始学习SpringMVC之前,我们需要先了解两个基本概念:三层架构和MVC模式。
三层架构
当我们开发服务器端程序时,通常会采用两种形式:C/S架构(客户端/服务器)和B/S架构(浏览器/服务器)。使用Java语言主要开发的是B/S架构程序,而B/S架构又分为三层:
-
表现层:WEB层,负责与客户端进行数据交互。表现层一般采用MVC设计模型。
-
业务层:处理具体的业务逻辑。
-
持久层:负责操作数据库。(增删改查)
MVC模型
MVC全称是Model View Controller(模型-视图-控制器),每个部分各司其职:
-
Model:数据模型,通常是JavaBean类,用于数据封装。
-
View:视图层,如JSP、HTML页面,用于展示数据给用户。
-
Controller:控制器,接收用户请求,控制整个流程,进行数据校验等。
📌 关系:MVC是表现层的实现方案,三层架构是整个程序的分层。
二、SpringMVC是啥?
-
本质:一个帮我们简化Servlet开发的框架(不用写一堆
if-else
判断请求路径)。 -
优点:通过注解配置路由,整合Spring容器,开发更高效。
三、SpringMVC入门案例
让我们一步步创建一个简单的SpringMVC应用:
创建WEB工程,引入开发的jar包
具体的坐标如下
<!-- 版本锁定 --><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>
编写index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>入门程序</title>
</head>
<body>
<%--超链接--%>
<h3>入门</h3>
<a href="/hello.do" >入门程序</a>
</body>
</html>
编写suc.jsp页面,路径为:/WEB-INF/pages/suc.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>成功</title>
</head>
<body><h3>入门成功了2...</h3></body>
</html>
编写Controller类和方法
package cn.tx.demo1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*** * * 控制器类,处理用户的请求*/
// 把当前类交给IOC容器进行管理
@Controller
public class HelloController {
/*** 处理超链接发送出来的请求* @return*/// 配置映射的配置@RequestMapping(path = "/hello.do")public String sayHello(){
System.out.println("入门方法执行了2...");
// 跳转的JSP页面的路径,默认使用的是请求的转发// return "/WEB-INF/pages/suc.jsp";
// 配置了视图解析器后,写法return "suc";}
}
配置核心的控制器(配置DispatcherServlet)
在web.xml配置文件中核心控制器DispatcherServlet
<!--配置前端控制器--><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--加载springmvc.xml配置文件,配置的是Spring配置--><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>*.do</url-pattern></servlet-mapping>
编写springmvc.xml的配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置spring创建容器时要扫描的包 --><context:component-scan base-package="com.qcbyjy"></context:component-scan><!-- 配置视图解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/"></property><property name="suffix" value=".jsp"></property></bean><!-- 配置spring开启注解mvc的支持 <mvc:annotation-driven></mvc:annotation-driven>-->
</beans>
启动Tomcat服务器,进行测试
执行流程分析
-
用户点击链接 →
http://localhost:8080/hello.do
-
Tomcat收到请求,交给
DispatcherServlet
处理。 -
DispatcherServlet
查地图(@RequestMapping
)找到HelloController
的sayHello()
方法。 -
方法执行后返回
"suc"
→ 视图解析器拼接成/WEB-INF/pages/suc.jsp
。 -
JSP渲染后返回给用户。
SpringMVC官方提供图形
入门案例中的组件分析
- 前端控制器(DispatcherServlet)
- 处理器映射器(HandlerMapping)
- 处理器(Handler)
- 处理器适配器(HandlAdapter)
- 视图解析器(View Resolver)
- 视图(View)
四、RequestMapping注解
RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系
RequestMapping注解可以作用在方法和类上
- 作用在类上:第一级的访问目录
- 作用在方法上:第二级的访问目录
- 细节:路径可以不编写 / 表示应用的根目录开始
RequestMapping的属性
- path 指定请求路径的url
- value value属性和path属性是一样的
- mthod 指定该方法的请求方式
- params 指定限制请求参数的条件
package cn.tx.demo1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/*** * 7* 角色模块的类*/
@Controller
@RequestMapping(path = "/role") // 一级请求路径
public class RoleController {
/*** /role/save.do* method="当前方法允许请求方式能访问"* params="请求路径上传参数"* @return*/@RequestMapping(path = "/save.do",method = {RequestMethod.GET},params = "username")public String save(){System.out.println("保存角色...");return "suc";}
@RequestMapping(value = "/delete.do")public String delete(){System.out.println("删除角色...");return "suc";}
}
五、请求参数的绑定
SpringMVC提供了强大的请求参数绑定功能,可以将请求参数自动绑定到方法参数上。
请求参数的绑定说明
-
绑定机制
- 表单提交的数据都是k=v格式的 username=haha&password=123
- SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
- 要求:提交表单的name和参数的名称是相同的
-
支持的数据类型
- 基本数据类型和字符串类型
- 实体类型(JavaBean)
- 集合数据类型(List、map集合等)
基本数据类型和字符串类型
- 提交表单的name和参数的名称是相同的
- 区分大小写
实体类型(JavaBean)
- 提交表单的name和JavaBean中的属性名称需要一致
- 如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 例如:address.name
给集合属性数据封装
- JSP页面编写方式:list[0].属性
1. 基本数据类型和字符串类型
@RequestMapping("/save1.do")
public String save(String username, Integer age) {System.out.println("姓名:"+username);System.out.println("年龄:"+age);return "suc";
}
表单的name属性需要与方法参数名称一致。
2. 绑定到JavaBean对象
@RequestMapping("/save2.do")
public String save2(User user) {System.out.println("user对象:"+user);return "suc";
}
表单的name属性需要与JavaBean中的属性名称一致。如果JavaBean包含其他引用类型,可以使用"对象.属性"的形式,如address.money。
3. 绑定到集合类型
对于集合类型,可以使用以下形式:
<input type="text" name="list[0].money" />
<input type="text" name="list[1].money" />
<input type="text" name="addressMap['home'].province" /><br/>
<input type="text" name="addressMap['home'].city" /><br/>
完整的案例如下:
jsp代码
<%--Created by IntelliJ IDEA.User: DellDate: 2025/4/27Time: 16:46To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>请求参数绑定</title>
</head>
<body><h3>请求参数绑定入门</h3><form action="/user/save1.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/><input type="submit" value="提交" /></form><h3>请求参数绑定(封装到实体类)</h3><form action="/user/save2.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/><input type="submit" value="提交" /></form><h3>请求参数绑定(封装到实体类)</h3><form action="/user/save3.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/>金额:<input type="text" name="address.money" /><br/><input type="submit" value="提交" /></form><h3>请求参数绑定(封装到实体类,存在list集合)</h3><form action="/user/save4.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/>金额:<input type="text" name="address.money" /><br/>集合:<input type="text" name="list[0].money" /><br/>集合:<input type="text" name="list[1].money" /><br/><input type="submit" value="提交" /></form><h3>请求参数绑定</h3><form action="/user/save5.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/>日期:<input type="text" name="birthday"><br/><input type="submit" value="提交" /></form><h3>请求参数绑定(封装到实体类,包含 Map)</h3><form action="/user/save7.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/>日期:<input type="text" name="birthday"><br/>家庭地址-省份:<input type="text" name="addressMap['home'].province" /><br/>家庭地址-城市:<input type="text" name="addressMap['home'].city" /><br/>公司地址-省份:<input type="text" name="addressMap['company'].province" /><br/>公司地址-城市:<input type="text" name="addressMap['company'].city" /><br/><input type="submit" value="提交" /></form><h3>使用Servlet原生的API</h3><a href="/user/save6.do">Servlet原生的API</a></body>
</html>
Javabean代码
package cn.tx.demo2;import org.springframework.format.annotation.DateTimeFormat;import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;public class User implements Serializable {private String username;private Integer age;//日期// 2000-11-11 格式的日期不能进行转换了,开发使用还是比较多,比较简单
// @DateTimeFormat(pattern = "yyyy-MM-dd")private Date birthday;//引用对象private Address address;private List<Address> list;//mapprivate Map<String,Address> addressMap;@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", birthday=" + birthday +", address=" + address +", list=" + list +", addressMap=" + addressMap +'}';}public Map<String, Address> getAddressMap() {return addressMap;}public void setAddressMap(Map<String, Address> addressMap) {this.addressMap = addressMap;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public List<Address> getList() {return list;}public void setList(List<Address> list) {this.list = list;}
}package cn.tx.demo2;import java.io.Serializable;public class Address implements Serializable {private String city;private String province;private Double money;public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", province='" + province + '\'' +", money=" + money +'}';}
}
controller代码
package cn.tx.demo2;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@Controller
@RequestMapping("/user")
public class UserController {/*** 请求参数的绑定* @return*/@RequestMapping("/save1.do")public String save(String username,Integer age){System.out.println("姓名"+username);System.out.println("年龄:"+age);return "suc";}/*** 请求参数的绑定* @return*/@RequestMapping("/save2.do")public String save2(User user){System.out.println("user对象:"+user);return "suc";}/*** 请求参数的绑定* @return*/@RequestMapping("/save3.do")public String save3(User user){System.out.println("user对象:"+user);return "suc";}/*** 请求参数的绑定* @return*/@RequestMapping("/save4.do")public String save4(User user){System.out.println("user对象:"+user);return "suc";}/*** 请求参数的绑定* @return*/@RequestMapping("/save5.do")public String save5(User user){System.out.println("user对象:"+user);return "suc";}/*** 测试map参数绑定* @return*/@RequestMapping("/save7.do")public String save7(User user){System.out.println("User with map:" +user);return "suc";}/*** 原生的API* @return*/@RequestMapping("/save6.do")public String save6(HttpServletRequest request, HttpServletResponse response){System.out.println(request);// 获取到HttpSession对象HttpSession session = request.getSession();System.out.println(session);System.out.println(response);return "suc";}}
请求参数中文乱码的解决
在web.xml中配置Spring提供的过滤器类
<!-- 配置过滤器,解决中文乱码的问题 --><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>
自定义类型转换器
第一种方式使用DateTimeFormat注解的方式
package cn.tx.demo2;import org.springframework.format.annotation.DateTimeFormat;import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;public class User implements Serializable {private String username;private Integer age;//日期// 2000-11-11 格式的日期不能进行转换了,开发使用还是比较多,比较简单@DateTimeFormat(pattern = "yyyy-MM-dd")private Date birthday;//引用对象private Address address;private List<Address> list;//mapprivate Map<String,Address> addressMap;@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", birthday=" + birthday +", address=" + address +", list=" + list +", addressMap=" + addressMap +'}';}public Map<String, Address> getAddressMap() {return addressMap;}public void setAddressMap(Map<String, Address> addressMap) {this.addressMap = addressMap;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public List<Address> getList() {return list;}public void setList(List<Address> list) {this.list = list;}
}
第二种方式,自定义类型转换器。如果想自定义数据类型转换,可以实现Converter的接口
自定义类型转换器
package cn.tx.demo2;import org.springframework.core.convert.converter.Converter;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class StringToDate implements Converter<String,Date> {public Date convert(String s) {// 判断if (s == null) {throw new RuntimeException("请输入内容");}// 进行转换SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");try {// 进行转换return sdf.parse(s);} catch (ParseException e) {throw new RuntimeException(e);}}
}
注册自定义类型转换器,在springmvc.xml配置文件中编写配置
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置spring创建容器时要扫描的包 --><context:component-scan base-package="cn.tx"></context:component-scan><!-- 配置视图解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/"></property><property name="suffix" value=".jsp"></property></bean><!--配置日期类型转换器,类型转换器的组件,把日期类型转换注入到组件对象中--><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><set><bean class="cn.tx.demo2.StringToDate" /></set></property></bean><!--让映射器、适配器和处理器生效(默认不配置也是可以的)--><mvc:annotation-driven conversion-service="conversionService"/><!--配置spring开启注解mvc的支持--><!--<mvc:annotation-driven></mvc:annotation-driven>-->
</beans>
在控制器中使用原生的ServletAPI对象
只需要在控制器的方法参数定义HttpServletRequest和HttpServletResponse对象
/*** 原生的API* @return*/@RequestMapping("/save6.do")public String save6(HttpServletRequest request, HttpServletResponse response){System.out.println(request);// 获取到HttpSession对象HttpSession session = request.getSession();System.out.println(session);System.out.println(response);return "suc";}
测试