1. SpringMVC介绍
MVC模型
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序的模式。主要目的就是为了分离Model模型和View视图,将数据的展示和数据的存储分离,核心是控制器,降低代码的耦合度,不同于三层架构。
序号 | MVC | 职责 |
---|---|---|
1 | Model(模型) | 存储数据:JavaBean 与数据库进行交互 |
2 | View(视图) | 展示数据:JSP+JSTL+EL HTML |
3 | Controller(控制器) | 控制器的三大作用: 1. 获取用户提交的数据 2. 调用业务层,获取Model中数据 3. 控制哪个页面或数据显示给用户 |
SpringMVC是什么
SpringMVC 是一种基于 MVC 设计模型的请求驱动类型的轻量级 Web 框架, 属于 Spring框架的后续产品。 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
SpringMVC 已经成为目前最主流的 MVC 框架之一, 从 Spring3.0 的发布, 就已全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。
2. SpringMVC的配置
步骤
1. 创建maven工程
2. 使用插件转换成web工程
3.添加pom.xml文件中依赖
<dependencies>
<!-- 会间接依赖spring-context, spring-web等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
4.准备页面:在pages目录下创建/success.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功</title>
</head>
<body>
<h2>操作成功</h2>
</body>
</html>
5.创建控制器:com.it.controller.HelloController
-
在类上添加@Controller注解
-
创建方法public String hello()
-
在方法上添加@RequestMapping("hello.do")注解
-
方法中返回"success"字符串
添加配置
springMVC.xml
在resources目录下创建此XML文件
配置以下内容
-
开启注解扫描, 识别Controller控制器所在的包
-
配置视图解析器
-
控制器返回字符串的路径前缀
-
控制器返回字符串的路径后缀
-
-
开启SpringMVC注解驱动
注:使用annotation-driven时,不要错选成cache命名空间,应该使用mvc的命名空间
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--1. 指定扫描基包:只扫描controller所在的包-->
<context:component-scan base-package="com.it.controller"/>
<!--2. 指定视图解析器,用来将逻辑地址转换成物理地址 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置拼接地址的前缀 -->
<property name="prefix" value="/pages/"/>
<!-- 配置拼接地址的后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!--3. 指定mvc注解的驱动,注:不要选错了-->
<mvc:annotation-driven/>
</beans>
web.xml
-
配置DispatcherServlet(前端控制器)
-
创建Spring容器加载的配置名称contextConfigLocation
-
创建Spring容器加载的配置文件classpath:springMVC.xml 默认加载的是/WEB-INF/servlet名字-servlet.xml这个配置文件
-
配置servlet加载顺序load-on-startup为1
-
-
配置dispatcherServlet请求路径的拦截,拦截所有的*.do请求
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 前端控制器,配置Servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 读取springMVC.xml配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- 在web容器启动的时候加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有*.do的访问地址都会由这个Servlet来处理-->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
在tomcat中部署运行
在浏览器中输入http://localhost:8080/hello.do
SpringMVC的执行流程分析
3.SpringMVC:三大组件
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于MVC模式中的C,dispatcherServlet是整个流程控制的中心(处理*.do的请求),由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
org.springframework.web.servlet.DispatcherServlet
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求url找到处理器中的方法 (hello.do --> HelloController中hello())
SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
具体的实现类是:RequestMappingHandlerMapping
处理器
也叫后端控制器,就是我们写的Controller类
是DispatcherServlet前端控制器的实际处理器(HelloController),对具体的用户请求进行处理。
HandlerAdapter:处理器适配器
通过HandlerAdapter对处理器执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
因为不同的处理器有不同的实现方式,有注解的方式实现的,有配置文件的实现的。我们需要统一的适配器来处理它们。就好比生活中不同的设备有统一的USB接口一样。
ViewResolver:视图解析器
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。(前缀+返回字符串+后缀)
具体实现类:InternalResourceViewResolver,根据处理器适配器执行的结果,由视图解析器渲染页面结果。
4. @RequestMapping注解使用
@RequestMapping的作用
作用
用来设置处理器中一个方法的访问地址
出现位置
用在方法和类上面
-
类:相当于一个模块的访问地址 /order
-
方法:指定这个方法的访问地址 /save
完整的访问地址: /模块地址/方法地址,如:/order/save
@Controller
@RequestMapping("/student") //模块访问地址
public class Demo1StudentController {
/**
* 方法访问地址
* @RequestMapping 属性:
* 1. path指定访问地址,同义词是value
* 2. method 限制请求的方法:GET或POST。如果被限制会出现405错误
* 3. param 限制提交的参数:
* 3.1) {"id","name"} 必须有这两个参数的名字,否则会出现400错误
* 3.2) {"id=1","name=newboy"} 不但要有这些参数,而且值还有限制
* 3.3) {"id!=1"} id不等于1的值都可以
*/
@RequestMapping(path = "/save", method = RequestMethod.GET, params = {"id!=1"})
public String save() {
System.out.println("保存学生");
return "success";
}
}
5. 参数绑定:Servlet对象作为参数
步骤
-
pom.xml中添加servlet的依赖
-
在控制器类中编写方法
-
使用Servlet对象做参数方法的参数
pom.xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
HttpServletRequest和HttpSession
需求
通过request,获取请求的参数数据
通过session,获取和保存会话域数据
步骤
-
创建String study()方法,访问地址设置为study
-
输出:调用学习方法
-
使用request对象获取浏览器提交的name参数值
-
将获取的值放在请求域中
-
在pages目录下创建student.jsp文件
-
跳转到student.jsp,使用EL在页面上显示
HttpServletResponse
需求
在方法中得到响应对象,并且使用
步骤
-
创建public void eat()方法,没有返回值,抛出IOException
-
设置访问地址为eat
-
方法使用HttpServletResponse对象做为参数
-
使用response设置响应类型,得到打印流
-
在浏览器上输出字符串
代码
/**
* 请求对象,会话对象可以直接使用
*/
@RequestMapping("/study")
public String study(HttpServletRequest request, HttpSession session) {
String id = request.getParameter("id");
request.setAttribute("id",id);
//直接使用会话域
session.setAttribute("name", "张三");
return "success";
}
/**
* 响应对象
*/
@RequestMapping("/eat")
public void eat(HttpServletResponse response) throws IOException {
System.out.println("调用了吃的方法");
response.sendRedirect("/pages/success.jsp");
}
JSP
请求域中值:${requestScope.id} <hr/>
会话域中值:${sessionScope.name} <hr/>
6. 键值对类型的参数绑定
Map/Model/ModelMap
介绍
-
Model是一个接口,ModelMap是一个类,都用于封装响应的模型数据。
-
使用Model和使用ModelMap,效果是一样的,它们使用的是相同的子类,两者没有继承关系。
-
使用Model封装响应的模型数据,页面视图可以返回字符串类型。
步骤
使用Map
-
创建方法String sport(Map<String,Object> map)
-
调用map的put()方法向请求域中添加键和值
-
返回字符串
-
在student.jsp页面上显示出来
使用Model
-
创建方法String swim(Model model),方法参数是Model
-
调用model的addAttribute()方法向请求域中添加键和值
-
返回字符串
-
在student.jsp页面上显示出来
使用ModelMap
-
创建方法String draw(ModelMap map),方法参数是ModelMap
-
调用map的addAttribute()方法向请求域中添加键和值
-
返回字符串
-
在student.jsp页面上显示出来
/**
* 绑定Map对象,如果向Map中添加键和值,就相当于向请求域中添加了键和值,降低了与Servlet对象的耦合度
* 可以使用Map, Model, ModelMap
*/
@RequestMapping("/sport")
public String sport(Map<String,Object> map) {
map.put("id", 100);
map.put("name", "李四");
System.out.println(map);
return "success";
}
@RequestMapping("/swim")
public String swim(Model model) {
//支持链式写法,可以添加多个键和值。也是添加到请求域中
model.addAttribute("id", 200).addAttribute("name", "孙悟空");
return "success";
}
@RequestMapping("/draw")
public String draw(ModelMap model) {
//支持链式写法,可以添加多个键和值。也是添加到请求域中
model.addAttribute("id", 300).addAttribute("name", "猪八戒");
return "success";
}
7. 参数绑定:简单类型
/**
* 参数的绑定
*/
@Controller
@RequestMapping("/user")
public class Demo2UserController {
/**
* 简单类型可以直接封装到方法中去
* @param id 建议使用包装类型,如果没有值就为NULL,否则会出现500错误,会尝试将一个null转成int
*/
@RequestMapping("/save")
public String save(Integer id, String name) {
System.out.println("保存用户id: " + id + ",name: " + name);
return "success";
}
}
访问地址
http://localhost:8080/user/save.do?id=1&name=xiaoming
结果
保存用户id: 1,name: xiaoming
8. 参数绑定:参数乱码问题
解决方案
Spring提供了统一的编码过滤器CharacterEncodingFilter,解决提交数据中文乱码问题。
- 在web.xml中配置spring提供的CharacterEncodingFilter过滤器
-
指定初始参数名为:encoding,参数值为:utf-8
-
过滤地址为/*
CharacterEncodingFilter配置
<!-- 配置spring写的汉字乱码的过滤器 -->
<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>
9. 参数绑定:对象类型
POJO对象
要求
请求参数名称一定要和POJO对象的属性名一致
@RequestMapping("/update")
public String update(User user) {
//会自动封装对象所有的属性值
System.out.println(user);
return "success";
}
10.参数绑定:简单类型的数组和集合
访问地址:
处理器方法:
@RequestMapping("/delete")
public String delete(Integer[] id) {
System.out.println(Arrays.toString(id));
return "success";
}
/**
* @RequestParam添加到方法的参数上
*/
@RequestMapping("/list")
public String list(@RequestParam ArrayList<Integer> id) {
System.out.println(id);
return "success";
}
1.给数组参数能否直接绑定值?
可以,只要参数名相同就可以了
2.给集合参数能否直接绑定值?
不可以,需要指定@RequestParam注解放在方法的形参前面
11. 参数绑定:POJO对象中的集合属性
代码
-
Address
@Data
public class Address {
// 用户地址 - 省份
private String province;
// 用户地址 - 城市
private String city;
}
- User
@Data
public class User {
private Integer id;
private String name;
private Address address; //POJO类
private List<String> hobby; //字符串集合
private List<Address> addressList; //POJO类集合
private Map<String, Address> map; //Map集合
}
- 控制器方法
@RequestMapping("/update")
public String update(User user) {
//会自动封装对象所有的属性值
System.out.println(user);
return "success";
}
- 页面代码
<h2>包装类的封装</h2>
<form action="user/update.do" method="post">
<input type="text" name="id" value="1"> <br/>
<input type="text" name="name" value="白骨精"> <br/>
省:
<input type="text" name="address.province" value="广东"> <br/>
市:
<input type="text" name="address.city" value="广州"> <br/>
爱好:
<input type="checkbox" name="hobby" value="游泳">游泳
<input type="checkbox" name="hobby" value="上网">上网
<input type="checkbox" name="hobby" value="下棋">下棋 <hr/>
其它地址:<br/>
<input type="text" name="addressList[0].province" value="湖南省">
<input type="text" name="addressList[0].city" value="长沙"> <br/>
<input type="text" name="addressList[1].province" value="广西省">
<input type="text" name="addressList[1].city" value="柳州"> <br/>
Map集合:<br/>
<input type="text" name="map['hn'].province" value="湖南省">
<input type="text" name="map['hn'].city" value="长沙"> <br/>
<input type="text" name="map['gx'].province" value="广西省">
<input type="text" name="map['gx'].city" value="柳州"> <br/>
<input type="submit" value="提交">
</form>
12. 参数相关的注解:@RequestParam
@RequestParam注解
-
作用:放在方法的参数前面,用于提交的参数名与方法的形参不同的情况
-
如果没有提供参数:
-
int基本数据类型: 如果没有赋值会出现500
-
Integer包装数据类型: 如果没有赋值会为NULL
-
/**
* required 属性
* 默认是true,表示这个参数必须要有,如果没有提供报400
* 设置成false,没有就为null
* defaultValue 属性:
* 如果没有指定值,默认是NULL,可以给它指定一个默认值
*/
@RequestMapping("/find")
public String find(@RequestParam(name = "name", required = false, defaultValue = "Boy") String username) {
System.out.println("用户名:" + username);
return "success";
}