第三章、SpringMVC控制器开发详解
3.1 核心要点
3.2 控制器接受客户端(client)请求参数详解
3.2.1 回顾:Web开发中如何接受Client请求参数
3.2.2 基于Servlet API接受Client请求参数
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("param1")
public String param(HttpServletRequest request) {
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("name = " + name);
System.out.println("password = " + password);
return "param1";
}
}
**注意:这种方式虽然最为直观,简单,但是代码冗余且与ServletAPI存在耦合,所以在SpringMVC开发中并不建议使用**
3.2.3 基于简单变量接受Client请求参数
- 所谓简单变量:指的就是8种基本类型+String这些类型的变量。把这些类型的变量,作为控制器方法的形参,用于接受client提交的数据。
- 思路分析
-
代码
@Controller @RequestMapping("/param") public class ParamController { @RequestMapping("param2") public String param2(String name,String password) { System.out.println("name = " + name); System.out.println("password = " + password); return "param1"; } }
-
细节分析
-
常见类型自动转换
@RequestMapping("param3") public String param3(String name,int age) { System.out.println("name = " + name); System.out.println("age = " + age); return "param1"; } SpringMVC底层针对age接受数据时会自动调用 int age = Integer.parseInt("10")
- 常见类型泛指:8种基本类型及其包装器,String等常见类型。
- Date日期等特殊类型,默认不支持,需要程序员自定义类型转换器【后续讲解】
-
基本类型尽量使用包装器
客户端请求:http://localhost:8989/param/param/param3?name=jack @Controller @RequestMapping("/param") public class ParamController { @RequestMapping("param3") public String param3(String name,int age) { System.out.println("name = " + name); System.out.println("age = " + age); return "param1"; } } 程序报错:原因在于age没有提交内容,等同于null,而int是基本类型无法存储null值,所以报错 改正方式:1.age参数使用包装器类型,可以存储null值 2.为age参数设置默认值,需要@RequestParam注解配合使用。
-
3.2.4 基于POJO类型接受Client请求参数
- 什么是POJO
- POJO全称叫做 Plain Ordinary Java Object(简单的Java对象)
- POJO类型对象的特点是:
- 类型中如果存在成员变量,必须提供set get
- 提供默认无参构造
- 可以实现Serializable,也可以不实现
- 不实现容器或者框架所规定的接口
- 用户根据业务封装的实体,DTO这些类型就是POJO
- 使用场景
准备一个实体,成员变量名字要和表单的name对应,其次在控制器方法中,将POJO类作为形参类型定义在控制器方法参数中。
-
代码
// POJO public class User implements Serializable { private String name; private String password; private int age; // Controller @RequestMapping("param4") public String param4(User user) { System.out.println("user = " + user); return "param1"; } // 表单请求 <form method="post" action="${pageContext.request.contextPath}/param/param4"> UserName <input type="text" name="name"><br> Password <input type="text" name="password"><br> Age <input type="text" name="age"><br> <input type="submit" value="reg"> </form> **POJO的成员变量的名字要和请求参数的key或者表单name属性的值严格对应**
-
注意1.提交的表单中的name和控制器方法的name同名,那么谁会获取到内容?
http://localhost:8989/param/param/param4?name=小明&age=12&password=12 @RequestMapping("param4") public String param4(**String name**,User user) { System.out.println("user = " + user); return "param1"; } **name形参与user对象的name属性都会获取对应的内容,这个特定在后续前后端分离中会使用。**
-
注意2.
- 如果SpringMVC发现控制器形参类型,是8种基本类型+String的话,他会通过形参名与请求参数的key对象,接受数据
- 如果SpringMVC发现控制器形参类型,不是8种基本类型+String的话,他会通过形参类型的属性名与请求参数的key对象,接受数据
- 如果存在自定义类型转换器不适用于上述规律
3.2.5 接受一组简单变量的请求参数
- 使用场景
-
代码
@RequestMapping("param5") public String param5(Integer[] ids) { for (Integer id : ids) { System.out.println("id = " + id); } return "param1"; } // checkbox表单 <form method="post" action="${pageContext.request.contextPath}/param/param5"> <input type="checkbox" name="ids" value="1"><br> <input type="checkbox" name="ids" value="2"><br> <input type="checkbox" name="ids" value="3"><br> <input type="checkbox" name="ids" value="4"><br> <input type="checkbox" name="ids" value="5"><br> <input type="submit" value="reg"> </form>
-
细节分析 以List类举例
@RequestMapping("param6") public String param6(ArrayList<Integer> ids) { for (Integer id : ids) { System.out.println("id = " + id); } return "param1"; } 没有异常,但接收不到数据:SpringMVC会按照POJO的匹配方式,进行成员变量查找 @RequestMapping("param6") public String param6(List<Integer> ids) { for (Integer id : ids) { System.out.println("id = " + id); } return "param1"; } 抛出异常:SpringMVC无法提供具体的实现类、实例化形参
3.2.6 接收一组POJO类型对象的请求参数
- 使用场景
按照POJO的匹配方式,对于非8种基本类型的方法形参,只有将List实例化作为成员变量封装在POJO类中,并将这个类作为形参才能正确接收数据。
-
代码
**// POJO类** public class UsersDTO { private List<User> users = new ArrayList<>(); public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } } **//Controller类** @RequestMapping("param7") public String param7(UsersDTO usersDTO) { List<User> users = usersDTO.getUsers(); for (User user : users) { System.out.println("user = " + user); } return "param1"; } **//JSP页** <form method="post" action="${pageContext.request.contextPath}/param/param7"> UserName <input type="text" name="users[0].name"><br> Password <input type="text" name="users[0].password"><br> Age <input type="text" name="users[0].age"><br> <hr> UserName <input type="text" name="users[1].name"><br> Password <input type="text" name="users[1].password"><br> Age <input type="text" name="users[1].age"><br> <input type="submit" value="submit"> </form>
3.3 接收Client请求参数的总结
3.4@RequestParam注解
作用:用于修饰控制器方法的形参
@RequestMapping("param2")
public String param2(@RequestParam String name,@RequestParam String password)
3.4.1 @RequestParam注解详解
-
解决请求参数与方法形参名字不一致的问题
Http请求:http://localhost:8989/param/param1/param1?n=jack&p=1234 @RequestMapping("/param1") public String param1(@RequestParam("n") String name, @RequestParam("p") String password) { System.out.println("name = " + name); System.out.println("password = " + password); return "param1"; }
-
注意
-
@RequestParam注解简写形式:不书写value书写的内容,@RequestParam默认会把对应形参名作为value属性的值
-
使用了@RequestParam注解的形参,客户端必须传递数据,不能省略,否则报错。
-
POJO类型的形参,不能与@RequestParam注解联用。会报400错误
-
典型的应用场景
htpp请求:http://localhost:8989/param/paramController/param1?id≤10 @RequestMapping("/param1") public String param1(@RequestParam("id<") Integer id) { } **当提交的数据参数名不符合java的变量命名规则时,可以采用这种方式**
-
-
@RequestParam的required属性
required = true时:@RequestParam修饰的控制器方法参数,客户端必须提交数据,否则报错,默认值
required = false时:@RequestParam修饰的控制器方法参数,客户端可以不提交数据,不会报错
解决了使用@RequestParam注解时,客户端必须传递数据的问题。
-
@RequestParam的defaultValue属性
-
客户端没有提交数据的时候,给对应的形参提供默认值
http://localhost:8989/param/paramController/param1?name=jack @RequestMapping("/param4") public String param4(@RequestParam String name, @RequestParam(defaultValue="9999") String password) { System.out.println("name = " + name); System.out.println("password = " + password); return "param1"; } 注意:如果使用了defaultValue,则required属性也默认设置成了false
-
解决控制器方法形参,使用包装器的问题
@RequestMapping("param6") public String param6(@RequestParam(defaultValue = "0") int age) { System.out.println("age = " + age); return "param1"; }
-
defaultValue典型的使用场景
默认值操作,一个典型的应用场景是:分页首页查询,不传页号的设计
http://localhost:8989/param/page/findall http://localhost:8989/param/page/findall?pageNum=2 @RequestMapping("findall") public String findall(@RequestParam(defaultValue = "0") int pageNum) { }
-
3.5 中文请求参数的乱码问题
3.5.1 回顾JavaWeb开发中 中文乱码的解决方案
- GET请求乱码的解决方案
- POST请求乱码的解决方案
3.5.2 SpringMVC解决中文字符集乱码
-
GET请求的中文乱码
UTF-8字符集,Tomcat8已经内部处理,无需我们处理。GBK字符集或者Tomcat8以前版本,需要配置server.xml
-
POST请求的中文乱码,SpringMVC提供了过滤器解决
org.springframework.web.filter.CharacterEncodingFilter
-
web.xml配置(通过alt+insert插入)
<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>
3.6 SpringMVC的类型转换器
3.6.1 SpringMVC中的内置类型转换器
- 分析
-
内置类型转换器的概念
-
SpringMVC中提供了内置类型转换器,把客户端提交的字符串类型的请求参数,转换承控制器方法参数需要的数据类型
-
SpringMVC并不是对于所有的类型,都提供了内置的类型转换器,他只是提供了常见类型的转换器
比如:8种基本类型,常见的集合类型等
-
-
原理分析
在SpringMVC启动时,会通过mvc:annotation-driven/把FormattingConversionServiceFactoryBean。引入到SpringMVC体系中。FormattingConversionServiceFactoryBean存储了SpringMVC种所有的内置类型转换器。后续client提交请求参数时,如果对于控制器方法形参不是字符串类型,那么FormattingConversionServiceFactoryBean就会调用对应的类型转换器,进行类型转换,最终完成控制器方法形参的赋值。
3.6.2 SpringMVC中自定义类型转换器
SpringMVC在接收客户端提交请求参数时,如果请求参数对应的控制器方法形参,是非常规数据类型,SpringMVC默认情况下无法进行类型转换。会抛出异常,程序员可以通过自定义类型转换器解决上述问题。例如:日期类型
- 开发思路
-
编码
**// 实现Converter<S,T>接口** public class DateConverter implements Converter<String, Date> { @Override public Date convert(String source) { Date date = null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } } // **配置类型转换器,让Spring进行对象的创建** <bean id="dateConverter" class="com.baizhi.DateConverter"/> // **进行类型转换器注册,让SpringMVC能够识别日期类型转换的类型转换器-->** <bean id="serviceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="dateConverter"/> </set> </property> </bean> // **最后一步** <mvc:annotation-driven conversion-service="serviceFactoryBean"/> // **上述第一步 第二步可以合二为一** <bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean id="converter" class="com.baizhi.DateConverter"/> </set> </property> </bean>
3.7 接收其他请求数据
3.7.1 动态参数收集
-
单值动态参数收集(一个key对应一个请求数据)
http://localhost:8989/param/param2/param1?name=suns http://localhost:8989/param/param2/param1?age=10 @RequestMapping("/param1") public String param1(@RequestParam Map<String, String> params) { Set<String> keys = params.keySet(); for (String key : keys) { System.out.println("key = " + key+",value = "+params.get(key)); } return "param1"; } **注意:如果需要接收动态参数,必须保证Map的形参前面加入@RequestParam注解,否则接收不到数据**
-
多值动态参数收集(一个key对应多个请求数据)
http://localhost:8989/param/param2/param1?id=2&id=3&id=4 **第一种解决思路:通过数组形参接收请求参数** @RequestMapping("param5") public String param5(Integer[] ids) { for (Integer id : ids) { System.out.println("id = " + id); } return "param1"; } 存在的问题:请求参数的key发生改变就接收不到请求数据 第二种解决思路:通过MultiValueMap<K,V> 接收请求参数 @RequestMapping("/param2") public String param2(@RequestParam MultiValueMap<String, String> params) { Set<String> keys = params.keySet(); for (String key : keys) { List<String> values = params.get(key); System.out.println("key = " + key); for (String value : values) { System.out.println("value = " + value); } } return "param1"; }
-
典型应用场景
- 回顾Servlet中获取的方式
-
SpringMVC获取Cookie的方式
测试Cookie 通过postman测试 @RequestMapping("/param4") public String param4(@CookieValue("name") String value) { System.out.println("value = " + value); return "param1"; } 推荐使用@CookieValue获取cookie @RequestMapping("/param3") public String param3(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { if ("name".equals(cookie.getName())) { System.out.println("cookie.getValue() = " + cookie.getValue()); } } return "param1"; } 这种方式基于Servlet API存在耦合,不推荐使用。
3.7.3 接收请求头数据
- 什么是请求头
- 获取请求头的方式
-
Servlet中的获取方式
核心代码 String value = request.getHeader("key");
-
SpringMVC中的获取方式
@RequestMapping("/param6") public String param6(@RequestHeader("Host") String host) { System.out.println("host = " + host); return "param1"; } 推荐使用@RequestHeader的方式获取请求头 @RequestMapping("/param5") public String param5(HttpServletRequest request) { String host = request.getHeader("Host"); System.out.println("host = " + host); return "param1"; } 这种方式存在与Servlet API的耦合问题,不建议后续使用
-
下一章:Spring MVC学习随笔-控制器(Controller)开发详解:调用业务对象、父子工厂拆分(applicationContext.xml、dispatcher.xml)
作者:扬眉剑出鞘
出处: https://www.cnblogs.com/eyewink/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/190522.html