说明:部分内容节选网络,只做学习总结使用,侵删
SpringMVC简介
什么是Spring MVC ?
- Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一部分,它和Struts2一样都属于表现层的框架。
MVC(Model模型 View 视图 Controller 控制器):这是一种软件架构思想,是一种开发模式,将软件划分为三种不同类型的模块,分别是模型,视图,和控制器。- 模型:用于封装业务逻辑处理(java类);
- 视图:用于数据展现和操作界面(Servlet);
- 控制器:用于协调视图和模型(jsp);
- 处理流程:视图将请求发送给控制器,由控制器选择对应的模型来处理;模型将处理结果交给控制器,控制器选择合适的视图来展现处理结果;
- 优点:方便测试和代码的维护,分工协作等
SpringMVC工作原理
系统分层
系统分为:
- 表现层(UI):数据的展现,操作页面,请求转发。
- 业务层(服务层):封装业务处理逻辑
- 持久层(数据访问层):封装数据访问逻辑
各层之间的关系: 表现层通过接口调用业务层,业务层通过接口调用持久层,这样,当下一层发生变化改变,不影响上一层的数据。 MVC是一种表现层的架构思想:
SpringMVC五大组件
前端控制器 (DispatcherServlet)
映射处理器(HandlerMapping)
处理器(Controller)
模型和视图(ModelAndView)
视图解析器(ViewResolver)
SpringMVC具体工作流程
- 文字说明:
首先,用户浏览器发送请求,这个请求被前端控制器(或者说中央处理器)DispatcherServlet接收到,调用处理器映射器HandlerMapping,它会根据具体的请求找到相应的处理器Handler(由xml配置文件或者注解查找),封装成处理器对象及处理器拦截器(有则生成)返回给前端处理器。前端处理器拿到这个处理器对象,会去调用处理器适配器HandlerAdapter,处理器适配器根据适配调用具体的处理器(Controller–Handler),处理器处理这个请求,然后返回一个ModelAndView对象给处理器适配器HandlerAdapter,处理器适配器再接着返回给前端处理器,前端处理器则负责将ModelAndView交给视图解析器ViewReslover去解析,返回一个具体的view给前端处理器,前端处理器拿到view,渲染视图(将数据模型填充到视图中),响应给用户浏览器。
- 总结:
- DispatcherServlet 前端控制器
负责分发请求,springmvc框架提供
程序员不用写 - HandlerMapping 处理器映射器
负责根据请求路径查找处理器,springmvc框架提供
程序员不用写 - HandlerAdpater 处理器适配器
负责适配处理器,调用处理器相应功能方法,springmvc框架提供
系统架构师可以自定义,程序员不用写 - Handler处理器 也叫后端控制器 程序员写
- ModelAndView 模式和视图的综合体
springmvc框架提供
程序员不用写 - ViewResoler 视图解析器
负责将逻辑视图解析成真正的视图,springmvc框架提供
程序员不用写 - View视图 (jsp html pdf img)程序员自定义视图
Controller和Handler的区别?
controller指定的是类(控制器),handler指定的类中的一个方法(处理器)。
SpringMVC入门程序
@Controller和@RequestMapping
- @Controller 注解控制器
负责注册一个bean 到spring 上下文中
用@Controller注释的类叫控制器,是用来处理请求的类
- @RequestMapping 请求映射注解
用来注释请求的url地址和请求的提交方式
value属性的值用来配置请求URL
method可以用来限制请求方式,默认支持:DELETE POST GET PUT TRACE等
步骤
- 新建动态web项目0420springmvc_001
- 添加jar包到WebContent/WEB-INF/lib目录下,build path
- 在项目的src下添加springmvc-servlet.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans是springmvc配置文件的根标签 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd ">
<!-- 配置mvc的注解驱动 :不用自己配置处理器映射器、配置处理器适配器 -->
<mvc:annotation-driven />
<!-- 配置全局注解扫描 :不用自己配置处理器(Controller) -->
<!-- 等会所有的controller都放在com.xx.controller"这个包下-->
<context:component-scan base-package="com.xx.controller"></context:component-scan>
</beans>
- 修改web.xml文件,配置拦截器
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<!--所有的请求都交给DispatcherServlet中央处理器去处理-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--我们把springmvc-servlet.xml放到项目的src目录下,需要在这里进行配置-->
<!--它默认是去WebContent目录下去找-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
关于url-pattern的设置一般有以下几种方式:
配置springmvc的DispatcherServlet拦截的请求
/
所有的请求都交给DispatcherServlet进行处理,但是不包括*.jsp,支持RESTful风格的请求
/*
所有的请求都交给DispatcherServlet进行处理,包括 *.jsp
*.do
所有以.do结尾的请求都交给DispatcherServlet进行处理*.action
所有以.action结尾的请求都交给DispatcherServlet进行处理
- 新建测试类 com.xx.controller.PrintDateController.java
package com.xx.controller;
import java.io.IOException;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PrintDateController {
@RequestMapping(value="/printDate")
public String printDate(HttpServletResponse response) throws IOException{
// 打印当前日期到网页
response.getWriter().println(new Date());
return null;
}
}
- 发布项目到tomcat服务器,启动服务器,打开浏览器在地址栏输入:
http://localhost/0420springmvc_001/printDate
Controller方法的返回值类型
返回类型总共有三种:
- ModelAndView
- String
- void
Controller返回值类型是ModelAndView
当Handler的返回值类型是ModelAndView时,中央处理器收到这个返回值,会把它交给视图解析器进行解析,随后渲染页面,响应请求。
- 先在springmvc-servlet.xml配置ViewResolver视图解析器
<!-- 配置ViewResolver -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- prefix:表示页面的前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- suffix:表示页面后缀 -->
<property name="suffix" value=".jsp" />
</bean>
这样做的目的是给所有的逻辑视图加上前缀和后缀,变成一个完整的全限定名。
- 新建com.xx.controller.testReturnController.class, 下面所有的示例都写在这个类中,用@RequestMapping注解
@Controller
public class testReturnController {
// value只有一个值时可以省略value=
@RequestMapping("/modelAndView")
public ModelAndView modelAndView() {
// 创建ModelAndView对象
ModelAndView mav = new ModelAndView();
// 设置逻辑视图,全限定名是配置好的/WEB-INF/jsp/hello.jsp
mav.setViewName("hello");
// 可以存值,存储数据的范围与用法跟request.setAttribute("h","i am hello")一样
mav.addObject("h", "i am hello");
return mav;
}
}
- 新建WEB-INF/jsp/hello.jsp
<body>
i am hello.jsp
<hr/>
<!-- 取出mav里面存的值 -->
${h}
</body>
- 在浏览器中访问:
http://localhost/0420springmvc_001/modelAndView
Controller返回值类型是String
当Controller的返回值类型是String时,可以实现三种功能:
- 请求转发
- 重定向
- 返回逻辑视图名
请求转发
@RequestMapping("/returnStringForward")
public String returnStringForward(){
// 转发到WEB-INF/jsp/hello.jsp
return "forword:WEB-INF/jsp/hello.jsp";
}
重定向
@RequestMapping("returnStringRediredct")
public String returnStringRediredct(){
// 重定向到index.jsp
return "redirect:index.jsp"
}
注意,转发可以访问到任何资源,但是重定向只能访问到WebContent目录下的资源。
返回逻辑视图名
@RequestMapping("/returnStringModelView")
public String returnStringModelView(){
// hello是逻辑视图名,真正的视图是/WEB-INF/jsp/hello.jsp
return "hello";
}
Controller返回值类型是void
当返回值的类型是void,可以实现以下三种功能:
- 转发
- 重定向
- javabean—》json
转发与重定向
@RequestMapping("/voidF")
public void returnVoidF(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 请求转发到index.jsp,这种写法和普通的servlet写法无异
request.getRequestDispatcher("index.jsp").forward(request, response);
}
@RequestMapping("/voidR")
public void returnVoidR(HttpServletResponse response) throws IOException {
// 请求重定向到index.jsp
response.sendRedirect("index.jsp");
}
实体类对象解析为JSON数据返回
重点来了,怎么把一个实体类转化成一个json格式的数据返回是关键。
步骤:
- 添加jar包,SpringMvc解析JSON所依赖的jar包
- 新建实体类,这里用com.xx.entity.User来举例:
package com.xx.entity;
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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 User() {
super();
// TODO Auto-generated constructor stub
}
}
- 编写Handler:
@RequestMapping("/returnVoidJ")
public void returnVoidJ(HttpServletResponse response) throws IOException {
User user = new User();
user.setId(1);
user.setUsername("Bob");
user.setPassword("123456abcde");
// 将实体类解析为JSON数据
String userJson = JSONObject.fromObject(user).toString();
response.getWriter().println(userJson);
}
- 测试:
参数绑定
参数绑定指的是将请求参数的值绑定到controller方法参数的过程。
SpringMVC支持的参数绑定类型包括:
-
Servlet默认的数据类型
HttpServletRequest HttpServletResponse HttpSession -
Model或者ModelMap
-
java基础数据类型
byte short int long float double boolean char -
Java基础数据类型的封装类型
Byte Short Integer Long Float Double Boolean Charactor -
String
-
JavaBean
-
javaBean的封装类型
-
Java数组类型
我们一一来演示:
Servlet默认的数据类型
code和name假设是表单提交上来的参数。
你可以把Handler当做一个普通的Servlet来用,Servlet能做的事情它都可以做。我们可以拿到 HttpServletRequest,HttpServletResponse,HttpSession,并且调用它们各自的方法。
@RequestMapping("/paramBindingDefaultType")
public void paramBindingDefaultType(HttpServletRequest request, HttpServletResponse response, HttpSession session)
throws IOException {
String code = request.getParameter("code");
String name = request.getParameter("name");
session.setAttribute("code", code);
session.setAttribute("name", name);
response.sendRedirect("index.jsp");
}
Model或者ModelMap
Model和ModelMap都可以用来以键值对的形式保存数据,作用范围和request一样。
Model是接口,ModelMap是它的实现类。
@RequestMapping("/paramBindingModel")
public String paramBindingModel(HttpServletRequest request, Model model) {
String code = request.getParameter("code");
String name = request.getParameter("name");
model.addAttribute("code", code);
model.addAttribute("name", name);
// 转发到index.jsp,在里面用${code}、${name}取值
return "forward:index.jsp";
}
java基础数据类型、java基础数据的封装类型、String类型
比如现在有一个添加商品的页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>商品新增页面</title>
</head>
<body>
<h1 align="center">商品新增页面</h1>
<form action="paramBingBasicType" method="post" align="center"
style="text-align: center;">
<table border="1" width="500px" height="300px" align="center">
<tr>
<td>商品编号:</td>
<td><input type="text" name="code" size="25" /></td>
</tr>
<tr>
<td>商品名称:</td>
<td><input type="text" name="name" size="25" /></td>
</tr>
<tr>
<td>商品价格:</td>
<td><input type="text" name="price" size="25" /></td>
</tr>
<tr>
<td>商品数量:</td>
<td><input type="text" name="count" size="25" /></td>
</tr>
<tr>
<td>商品状态:</td>
<td><input type="radio" name="status" size="25" value="0"
checked="checked" />上架 <input type="radio"
name="status" size="25" value="1" />下架 </td>
</tr>
<tr>
<td>商品描述:</td>
<td><textarea rows="5" cols="25" name="description"></textarea>
</td>
</tr>
<tr>
<td>生产日期:</td>
<td><input type="date" name="product_date" size="25" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="保存" /></td>
</tr>
</table>
</form>
</body>
</html>
这张表单里面有code,name,price,count,status,description,product_date这些参数,表单提价到控制器paramBingBasicType去处,我们在这个接收一下这些参数(不包括product_date,它是date类型,SpringMVC默认不对日期类型进行处理,所以我们在开发时需要对日期类型进行处理。)
@RequestMapping("/paramBingBasicType")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
public String paramBingBasicType(String code,String name,double price,int count,Integer status,String description){
// 只要我们这样定义方法,表单上的参数就可以自动绑定到这些变量上,我们不需要做任何处理
// 这些变量的类型要和表单上的参数类型符号,变量名要和参数名一致(不一致也可以,但要另做处理)
// 打印一下看看:
System.out.println(code+"\t"+name+"\t"+price+"\t"+status+"\t"+description);
}
这是我们提交的表单:
点击保存后,控制台:
001 product1 30.0 2 0 test
JavaBean
实际开发中我们一般把上面这些参数封装成实体类去取值。所以我们要新建实体类Product.java,属性和绑定参数一致(类型和变量名都要一样),需要注意的是要对date类型做处理,在属性定义的上面加上注解,这样就可以正确映射到表单上的product_date参数:
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date product_date;
还是上面的那张表单,我们现在来用Javabean来绑定参数,注意要更改将表单上action="bindingJavaBean"
,把数据提交给bindingJavaBean处理:
@RequestMapping("/bindingJavaBean")
public void bindingJavaBean(Product product) {
// 我们什么都不用做,SpringMVC会自动帮我们把实体类对象product和表单上的参数绑定起来
// 打印一下看看
System.out.println(product.getCode());
System.out.println(product.getName());
System.out.println(product.getProduct_date());
}
访问表单:
表单提交给bindingJavaBean,控制台打印:
002
product2
Mon Apr 22 00:00:00 CST 2019
JavaBean的封装类型
- 新建Order类封装User类:
package com.xx.entity;
public class Order {
private String order_code;
private User user;
public String getOrder_code() {
return order_code;
}
public void setOrder_code(String order_code) {
this.order_code = order_code;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Order() {
super();
// TODO Auto-generated constructor stub
}
}
- 比如我现在有一个根据用户名查询订单的界面,这里要注意订单编号和所属用户的name值
order_code
和user.username
,前者没什么好说的,后者:user是Oreder的属性,username又是user的属性。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 align="center">查询用户订单信息</h1>
<form action="paramBindingPackObjectType" method="post">
<table border="1px" width="350px" height="200px" align="center"
style="text-align: center;">
<tr>
<td>订单编号:</td>
<td><input type="text" name="order_code" size="25" /></td>
</tr>
<tr>
<td>所属用户:</td>
<td><input type="text" name="user.username" size="25"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="查询" /></td
</tr>
</table>
</form>
</body>
</html>
- Handler,显然,Order类是User类的封装类,我们就可以通过Order类拿到请求的信息存到模型里,转发到index.jsp页面,在jsp页面用
${order.order_code}
和${order.user.username}
就可以取到。
@RequestMapping("/paramBindingPackObjectType")
public String paramBindingPackObjectType(Order order, Model model) {
model.addAttribute("order", order);
return "forward:index.jsp";
}
java数组类型
SpringMVC同样也支持java数组类型的绑定,比如我要实现一个批量删除用户信息的需求,就可以通过数组拿到这一批用户的id值。
- batchDeleteUser.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 align="center">批量删除用户信息</h1>
<form action="paramBindingListType" method="post">
<table border="1px" width="300px" height="400px" style="text-align: center;" align="center">
<tr>
<td>全选</td>
<th>用户ID</th>
<th>用户名</th>
<th>用户密码</th>
</tr>
<tr>
<td><input type="checkbox" name="ids" value="1"/></td>
<td>1</td>
<td>张三</td>
<td>admin</td>
</tr>
<tr>
<td> <input type="checkbox" name="ids" value="2"/></td>
<td>2</td>
<td>李四</td>
<td>admin4</td>
</tr>
<tr>
<td> <input type="checkbox" name="ids" value="3"/></td>
<td>3</td>
<td>王五</td>
<td>wangw</td>
</tr>
<tr>
<td> <input type="checkbox" name="ids" value="4"/></td>
<td>4</td>
<td>赵6</td>
<td>zhao6</td>
</tr>
<tr>
<td colspan="4"><input type="submit" value="批量删除"/></td>
</tr>
</table>
</form>
</body>
</html>
- 数据提交到控制器paramBindingArrayType,用数组接收ids要和表单上的name一样。
@RequestMapping("/paramBindingArrayType")
public String paramBindingArrayType(int[] ids) {
if(ids.length!=0){
for (int i = 0; i < ids.length; i++) {
System.out.println(ids[i]);
}
}
return "forward:index.jsp";
}
测试一下:
- 控制台:
SpringMVC不支持类表类型的参数绑定
虽然这样,但是我们可以把列表封装起来,比如新建一个UserVO:
package com.xx.entity;
import java.util.List;
public class UserVO {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public UserVO() {
super();
// TODO Auto-generated constructor stub
}
}
- 这是批量更改用户信息的jsp(注意name的写法):
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="paramBindingListType" method="post">
<table border="1px" width="300px" height="400px" style="text-align: center;" align="center">
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>用户密码</th>
</tr>
<tr>
<td><input type="text" name="users[0].id" size="25" value="1" readonly="readonly"/></td>
<td><input type="text" name="users[0].username" size="25" value="zhang3"/></td>
<td><input type="password" name="users[0].password" size="25" value="zhang3"></td>
</tr>
<tr>
<td><input type="text" name="users[1].id" size="25" value="2" readonly="readonly"/></td>
<td><input type="text" name="users[1].username" size="25" value="li4"/></td>
<td><input type="password" name="users[1].password" size="25" value="li4"></td>
</tr>
<tr>
<td><input type="text" name="users[2].id" size="25" value="3" readonly="readonly"/></td>
<td><input type="text" name="users[2].username" size="25" value="wang5"/></td>
<td><input type="password" name="users[2].password" size="25" value="wang5"></td>
</tr>
<tr>
<td><input type="text" name="users[3].id" size="25" value="4" readonly="readonly"/></td>
<td><input type="text" name="users[3].username" size="25" value="zhao6"/></td>
<td><input type="password" name="users[3].password" size="25" value="zhao6"></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="批量修改"/></td>
</tr>
</table>
</form>
</body>
</html>
- Handler:
@RequestMapping("/paramBindingListType")
public String paramBindingListType(UserVO userVO, Model model) {
List<User> users = userVO.getUsers();
model.addAttribute("users", users);
for (User user : users) {
System.out.println(user.getId() + " " + user.getUsername() + " " + user.getPassword());
}
return "forward:index.jsp";
}
测试:
- 控制台: