学习SpringMVC第二篇
前言 上一篇我们通过SpringMVC框架的快速入门已经了解了它的工作流程和各个组件,知道了引入SpringMVC框架后需要我们程序员手动开发的其实就是Handler 处理器和 View 视图,这一篇我们就从Handler 处理器(Controller层)开始入手这篇文章主要讲解的知识点如下:
- 参数绑定
- 简单数据类型
- POJO实体类型
- 集合类型
- SpringMVC编码过滤器
- SpringMVC日期转换格式器
- 数据回显
- Model的使用
- 转发、重定向
- 返回JSON文本
- 文件上传
参数绑定
当项目中引入springmvc框架后,所有的请求流转将由springmvc进行控制,当客户端发送的请求中包含数据(也就是请求参数)时,那么该如何在controller层获取这些参数呢?
在 SpringMVC 中,提交请求的数据是通过方法形参来接收的。
也就是说从客户端请求的 key/value 数据会自动的和方法的参数进行匹配,然后将 key/value 数据绑定到 Controller 的形参上,我们就可以在Controller层使用这些请求中的参数,这就叫做参数绑定
在参数绑定过程中,会涉及到参数绑定组件,可以理解为将请求的数据类型转换成我们需要的数据类型,也叫做参数绑定转换器。在SprinifMVC框架中内置了很多参数转换器,只有在需要的情况下才需要我们自定义参数转换器,这个后面会介绍到 默认支持的参数类型 SpringMVC有默认支持的参数类型,我们可以直接在Controller层形参上给出这些默认对象的声明,就可以直接使用这些对象对象 | 说明 |
---|---|
HttpServletRequest | 通过request对象获取请求信息例如url、ip、请求方式、请求参数和给未来页面传递数据 |
HttpServletResponse | 通过response对象获取请求信息例如设置响应类型、生成验证码、Cookie和返回json格式数据 |
HttpSession | 通过session对象获取session里存储的信息例如对会话级别的数据进行读写 |
Model | 通过model对象将数据填充到request域中向页面传递数据 |
接下来我介绍一下其他常用类型的参数绑定
为此我们先创建一个简单的表达页面用于提交数据
编辑index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>参数绑定title>head><body><form action="/param" method="post"> 用户名:<input type="text" name="username"><br/> 密码:<input type="text" name="password"><br/> <input type="submit" value="提 交">form>body>html>
简单数据类型参数绑定
基本数据类型
基本数据类型包括byte、short、int、long、float、double、boolean、char八种,接下来我们以 int 为例
Controller控制层代码
@Controllerpublic class ParamController { /** * 简单类型参数 * @return */ @RequestMapping("/param") public ModelAndView param(int username, int password) { System.out.println(username + " : " + password); return null; }}
测试,输出结果
控制台打印出来了我们提交的表单数据
注意:表单中input的name值是和控制层的参数变量名保持一致,这样才能 完成参数绑定。如果不一致的情况下就需要我们引入一个注解
@RequestParam
使用这个注解,控制层可以使用任意形参,但是必须要保证注解的value值和表单的input的name值保持一致
@RequestMapping("/param") public ModelAndView param(@RequestParam("username") int name, int password) { System.out.println("用户名:" + name); System.out.println("密码:" + password); return null; }
控制台成功打印出我们的提交数据
@RequestParam 注解细节
该注解有三个变量:value、required、defaultValue
value : 指定name属性的值是什么,一般情况下value属性可以省略不写,直接写值即可
required : 参数是否必须传递,可以是true或false
defaultValue : 设置默认值
包装数据类型
包装数据类型包括Integer、Long、Byte、Double、Float、Short,(String 类型在这也是适用的),接下来我们以 Integer和String 为例
Controller控制层代码
@RequestMapping("/param") public ModelAndView param(String username, Integer password) { System.out.println("用户名:" + username); System.out.println("密码:" + password); return null; }
和基本数据类型基本一样,如果我们在表单处没有提交任何一个参数,那么String类型的数据就会显示为"",Integer类型的数据就会显示为null
SpringMVC编码过滤器思考:
之前我们测试的参数绑定中,在Controller控制层没有对编码进行任何的操作,如果我们接收的是中文字符会是什么样子的呢?
唷~乱码了!
我们想到之前学过Servlet,可以设置request对象编码
request.setCharacterEncoding("utf-8”);
测试
唷~还是乱码!
因为SpringMVC框架接收参数是通过控制器中的无参构造方法,再经过具体业务方法的Object对象来得到具体的参数类型的解决方法:SpringMVC已经给我们准备了一个编码过滤器,该过滤器只针对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>
这样就可以正常提交中文字码了
POJO实体类型参数绑定
当需要接收客户端发送过来的多个数据时,我们在控制层通过声明方法参数一个一个的去接收就变的非常麻烦
我们是否可以在方法上声明对象类型的参数,通过对这些数据进行统一的接收,SpringMVC框架会自动的将接收过来的数据封装到对象中
普通POJO类型
- 我们先定义一个User类
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String username; private Integer password;}
注意:表单中input中的name属性只要和POJO实体类中的属性保持一致就可以映射成功
编辑Controllerl类
/** * pojo实体类型参数 * @return */ @RequestMapping("/param") public ModelAndView param(User user) { System.out.println(user); return null; }
- 测试,输出结果
接下来我们在User实体中新添加一个成员变量(Date类型)
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String username ; private String password; private Date birthday;}
index.jsp表单提交页面中也添加一个input标签
生日:type="text" name="birthday">
测试,输出结果
- 测试,输出结果
结果出问题了,SpringMVC不支持这种类型的数据
解决方法:SpirngMVC提供了一个@InitBinder注解,用于指定自定义的日期转换格式,因此,我们只需要在Controller类中添加下面的代码即可,在接受日期类型的参数时,会自动按照自定义的日期格式进行转换。 /* 自定义日期转换格式 */ @InitBinder public void InitBinder (ServletRequestDataBinder binder){ binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true) ); }
再次测试,输出结果
SpringMVC日期格式转换器
自定义转换器
import org.springframework.core.convert.converter.Converter;/** * 日期格式转换器 * 需要实现Converter接口,将String类型转换成Date类型 */public class DateConverter implements Converter<String,Date> { @Override public Date convert(String date) { //实现将字符串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { return dateFormat.parse(date); } catch (ParseException e) { e.printStackTrace(); } //参数绑定失败返回null return null; }}
配置dispatcher-servlet.xml
<context:component-scan base-package="com.cn"/> <mvc:annotation-driven conversion-service="conversionService">mvc:annotation-driven> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.cn.util.DateConverter">bean> list> property> bean>
- 再次测试,输出结果
新编辑一个实体类UserInfo
@Data@AllArgsConstructor@NoArgsConstructorpublic class UserInfo { private String address;}
- 在User实体中增加一个属性
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String username; private String password; private Date birthday; private UserInfo userinfo;}
index.jsp表单提交页面中也添加一个input标签
地址:type="text" name="userinfo.address">
注意:User对象中有userinfo属性,在表单提交代码中要使用 "属性名(对象类型的属性).属性名 "来定义input标签中的name值
测试代码
/** * 包装POJO类型参数绑定 * @param user * @return */ @RequestMapping("/param") public ModelAndView getParam(User user){ System.out.println(user.getUserinfo().getAddress()); return null; }
输出结果
编辑index.jsp表单提交页面(模拟选中多个复选框)
type= type= type=
编辑Controller层
@RequestMapping("/param") public ModelAndView param(String[] hobbies) { for (String hobby : hobbies) { System.out.println(hobby); } return null; }
Controller层的方法形参值和表单提交页面input标签中的name值保持一致
- 测试,输出结果
创建一个UserList类,封装 List 属性
public class UserList { private Listlist;}
index.jsp表单提交页面模拟提交多条用户信息
用户名:<input type="text" name="list[0].username"><br/> 密码:<input type="text" name="list[0].password"><br/> 用户名:<input type="text" name="list[1].username"><br/> 密码:<input type="text" name="list[1].password"><br/>
编辑Controller层
@RequestMapping("/param") public ModelAndView param(UserList userList) { List users = userList.getList(); for (User user : users) { System.out.println(user); } return null; }
测试,输出结果
创建一个UserMap类,封装 Map 属性
@Data@AllArgsConstructor@NoArgsConstructorpublic class UserMap { private Map<String,User> map;}
index.jsp表单提交页面模拟提交多条用户信息
用户名:<input type="text" name="map['one'].username"><br/> 密码:<input type="text" name="list['one'].password"><br/> 用户名:<input type="text" name="list['two'].username"><br/> 密码:<input type="text" name="list['two'].password"><br/>
- 编辑Controller层
@RequestMapping("/param") public ModelAndView param(UserMap userMap) { Map map = userMap.getMap(); for (Map.Entry entry : map.entrySet()) { System.out.println(entry); } return null; }
- 测试,输出结果
Model中添加属性
/** * 测试Model对象 */ @RequestMapping("/testModel") public String testModel(Model model){ /* 往Model添加属性 */ model.addAttribute("name", "桔子"); model.addAttribute("age", 18); return "home"; }
编辑home.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Titletitle>head><body> 用户名:${ name } <br/> 年龄:${ age }body>html>
测试,输出结果
编辑测试请求转发方法
/** * 测试请求转发(forward) */ @RequestMapping("/testForward") public String testForward(){ System.out.println("测试请求转发(forward)..."); return "forward:/hello"; } @RequestMapping("/hello") public String hello() { return "hello"; }
访问测试,输出结果
request.getRequestDispatcher("url").forward(req, resp);
特点:
- 转发是一次请求,一次响应
- 转发后地址栏没有发生变化,还是访问的/testForword地址
- 转发前后的request和response对象也是同一个
编辑测试重定向方法
/* * 测试请求重定向(redirect) */ @RequestMapping("/testRedirect") public String testRedirect(){ System.out.println("测试请求重定向(redirect)..."); return "redirect:/hello"; }
访问测试,输出结果
response.sendRedirect(url)
特点:
- 重定向是两次请求,两次响应
- 重定向后地址栏发生了变化,之前访问的/testRedirect地址,变成了/hello地址
- 重定向前后的request和response对象不是同一个
JSON(JavaScript Object Notation)是一种JS提供的轻量级的数据交换格式。
JSON在项目开发中是一种非常流行的数据交换格式。
我们如果想要返回给页面一个JSON文本,SpringMVC给我们提供了一个非常简便的注解——@ResponseBody首先我们需要添加两个JSON的jar包
<dependency> <groupId>org.codehaus.jacksongroupId> <artifactId>jackson-core-aslartifactId> <version>1.9.13version> dependency> <dependency> <groupId>org.codehaus.jacksongroupId> <artifactId>jackson-mapper-aslartifactId> <version>1.9.13version> dependency>
编辑测试方法
@RequestMapping("/testJson") @ResponseBody public List testJson(){ //模拟查询所有用户,将所有用户信息封装到List集合中 Listlist = new ArrayList(); list.add( new User("张三", 123) ); list.add( new User("李四", 222) ); //将所有用户的List集合以JSON格式响应 return list; }
输出结果
文件上传
文件的上传和下载基本上是web项目中经常中会用到的技术,比如在一些社交网站上上传一些图片和视频等springmvc中由MultipartFile接口来实现文件上传。
首先我们需要添加两个文件上传的jar包
<dependency> <groupId>commons-fileuploadgroupId> <artifactId>commons-fileuploadartifactId> <version>1.3.1version> dependency> <dependency> <groupId>commons-iogroupId> <artifactId>commons-ioartifactId> <version>2.4version> dependency>
配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="5242880">property> bean>
编辑前端upload.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>文件上传title>head><body><form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="picture"> <input type="submit" value="图片上传">form>body>html>
处理层Controller代码
@Controllerpublic class UploadController { @RequestMapping("/upload") //MultipartFile该对象就是封装了图片文件 public void upload(MultipartFile picture) { System.out.println(picture.getOriginalFilename()); } @RequestMapping("/testUpload") public ModelAndView testUpload() { return new ModelAndView("upload"); }}
- 访问测试,输出结果