4.SpringMVC的使用(1)
1、SpringMVC请求参数的处理
在之前的servlet中我们可以通过request.getParameter()来获取请求中的参数,但是在我们编写的SpringMVC的应用程序中,在具体请求的方法中并不包含request参数。采用注解的方式获取请求参数
-
如何获取SpringMVC中请求中的信息
默认情况下,可以直接在方法的参数中填写跟请求一样的名称,此时会默认接受参数如果有值,直接赋值,如果没有,那么直接给空值。
-
需要使用的有以下几个注解
- @RequestParam:获取请求的参数
- @RequestHeader:获取请求头信息
- @CookieValue:获取Cookie中的值
1、@RequestParam
-
功能:获取请求中的参数值,使用此注解之后,参数的名称不需要跟请求的名称一致,但是必须要写
public String request(@RequestParam("user") String username) //相当于servlet中的request.getParameter("user");
-
此注解还包含三个参数
- value:表示要获取的参数值
- 表示此参数是否必须,默认是true,如果不写参数那么会报错,如果值为false,那么不写参数不会有任何错误
- defaultValue:如果在使用的时候没有传递参数,那么定义默认值即可
-
演示
@RequestMapping(value = "/request") public String rest(@RequestParam(value = "name",required = false) String username){ System.out.println( username); return "success"; }
2、@RequestHeder
-
功能:获取请求头信息
public String header(@RequestHeader("User-Agent") String agent) //request.getHeader("User-Agent") //User-Agent:告知访问者是用什么工具类请求的
-
三个参数:
- value:表示要获取的参数值(请求头),如果要获取请求头中没有的信息,那么此时会报错
- 表示此参数是否必须,默认是true,如果不写参数那么会报错,如果值为false,那么不写参数不会有任何错误
- defaultValue:如果在使用的时候没有传递参数,那么定义默认值即可
-
演示
@RequestMapping(value = "/request") public String rest(@RequestHeader(value = "User-Agent",required = false) String Agent){ System.out.println( Agent); return "success"; }
3、@CookieValue
-
功能:获取Cookie中的值,如果要获取cookie中没有的信息,那么此时会报错,同样,此注解中也包含三个参数,跟@RequestParam一样。
public String cookie(@CookieValue("JSESSIONID") String id) //相当于servlet中的request.getCookies();
-
三个参数
- value:表示要获取Cookie中的参数值,如果要获取cookie中没有的信息,那么此时会报
- 表示此参数是否必须,默认是true,如果不写参数那么会报错,如果值为false,那么不写参数不会有任何错误
- defaultValue:如果在使用的时候没有传递参数,那么定义默认值即可
-
演示
@RequestMapping("/cookie") public String cookie(@CookieValue("JSESSIONID") String id){ System.out.println(id); return "success"; }
2、请求参数的绑定
我们都知道,表单中请求参数都是基于 key=value 的。 SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
1、绑定说明
-
支持的数据类型
- 基本数据类型和String类型
- POJO类型(包括实体类)
- 数组和集合类型(数组,list,map)
-
使用要求
-
基本数据类型和String类型:要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
-
POJO类型:要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。
-
数组和集合类型:在表单中请求参数名称要和 POJO 中集合属性名称相同,给 List 集合中的元素赋值,使用下标。给 Map 集合中的元素赋值,使用键值对。
-
2、基本数据类型和String类型的演示
-
访问:http://localhost:8080/date?id=1&name=haha
-
代码:
@Controller public class datatypecontroller { @RequestMapping("/date") public String data(Integer id, String name) { System.out.println(id + " " + name); return "success"; } }
3、POJO类型的演示
-
不含集合的POJO类
-
创建POJO类
user.java
package com.mvc.bean; public class User { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
Address.java
public class Address { private String province; private String city; private String town; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getTown() { return town; } public void setTown(String town) { this.town = town; } @Override public String toString() { return "Address{" + "province='" + province + '\'' + ", city='" + city + '\'' + ", town='" + town + '\'' + '}'; } }
-
控制层
@RequestMapping("/pojo") public String data(User user) { System.out.println(user); return "success"; }
-
创建login.jsp页面
<%-- Created by IntelliJ IDEA. User: Lenovo Date: 2021/3/22 Time: 10:57 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <% pageContext.setAttribute("ctp",request.getContextPath()); %> <body> <form action="${ctp}/pojo" method="post"> 编号:<input type="text" name="id"/><br> 姓名:<input type="text" name="name"/><br> 省份:<input type="text" name="address.province"/><br> 城市:<input type="text" name="address.city"/><br> 区域:<input type="text" name="address.town"/><br> <input type="submit" value="submit"/><br> </form> </body> </html>
-
访问,login.jsp,填入数据。访问成功。
-
-
含集合的POJO类演示
-
创建POJO类
package com.mvc.bean; public class Address { private String city; private String town; public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getTown() { return town; } public void setTown(String town) { this.town = town; } @Override public String toString() { return "Address{" + "city='" + city + '\'' + ", town='" + town + '\'' + '}'; } } ------------------------- package com.mvc.bean; import java.io.Serializable; import java.util.List; import java.util.Map; public class User { private Integer id; private String name; private List<Address> list; private Map<String, Address> map; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Address> getList() { return list; } public void setList(List<Address> list) { this.list = list; } public Map<String, Address> getMap() { return map; } public void setMap(Map<String, Address> map) { this.map = map; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", list=" + list + ", map=" + map + '}'; } }
-
-
控制层
@RequestMapping("/pojo") public String data(User user) { System.out.println(user); return "success"; }
-
login.jsp页面
<form action="${ctp}/pojo" method="post"> 编号:<input type="text" name="id"/><br> 姓名:<input type="text" name="name"/><br> 省份:<input type="text" name="list[0].city"/><br> 城市:<input type="text" name=list[0]town."/><br> 省份:<input type="text" name="map[one].city"/><br> 城市:<input type="text" name="map[one].town"/><br> <input type="submit" value="submit"/><br> </form>
-
访问login.jsp页面,填入数据,访问成功。
3、乱码问题的解决
1、GET请求
-
在server.xml文件中,添加URIEncoding=“UTF-8”
-
步骤:进入tomcat–》conf—》server.xml文件。修改
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />
2、POST请求
-
在web.xml中编写过滤器实现
<!--设置编码格式--> <filter> <filter-name>encoding</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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
注意:如果有其他的过滤器,编码格式过滤器应该放在第一位,否则会失效。
4.SpringMVC对原生API的支持
-
SpringMVC可以在参数上使用原生Servlet API
-
Servlet API
-
HttpServletRequest
-
HttpServletResponse
-
HttpSession
-
ava.security.Principal 安全协议相关
-
Locale:国际化相关的区域信息对象
-
InputStream
-
OutputStream
-
Reader Writer
-
-
演示
@RequestMapping("/api") public String api(HttpServletRequest request, HttpServletResponse response){ System.out.println(request); System.out.println(response); return "success"; }
5、常用注解(传递参数)
在刚开始的helloworld项目中,我们传递了参数回到我们页面,但是后续的操作都只是接受用户的请求,那么在SpringMVC中除了可以使用原生servlet的对象传递数据之外,还有什么其他的方式呢?
1、使用Model,Map,ModelMap传输数据到页面
-
演示
写好OutputController类,在jsp中用四种内置对象分别获取参数值,最终所有参数值都在request作用域中
@Controller public class OutputController { @RequestMapping("output1") public String output1(Model model){ model.addAttribute("msg","hello,Springmvc"); return "output"; } @RequestMapping("output2") public String output2(ModelMap model){ model.addAttribute("msg","hello,Springmvc"); return "output"; } @RequestMapping("output3") public String output1(Map map){ map.put("msg","hello,Springmvc"); return "output"; } }
-
jsp有4大作用域,分别是page、reques、session、application,经过测试,所有的是参数值都会到request作用域中。上面的三个对象是什么关系呢?
2、使用ModelAndView对象传输数据到页面
-
演示
@Controller public class OutputController { @RequestMapping("mv") public ModelAndView mv(){ ModelAndView mv = new ModelAndView(); mv.setViewName("output"); mv.addObject("msg","hello.modelAndView"); return mv; } }
-
发现当使用modelAndView对象的时候,返回值的类型也是此对象,可以将要跳转的页面设置成view的名称,来完成跳转的功能,同时数据也是放到request作用中。
3、使用session传输数据到页面
-
由于上面我们都知道。将数据传输到页面,使用的是map、model、modelmap,三种方法都是将数据存入request作用域中,那我们想要将数据存入session中怎么办呢,为什要将数据存入session中,因为session可以存储用户的个人信息。此时可以使用SessionAttribute注解。
-
@SessionAttribute:此注解可以表示,当向request作用域设置数据的时候同时也要向session中保存一份,此注解有两个参数,一个value(表示将哪些值设置到session中),另外一个type(表示按照类型来设置数据,一般不用,因为有可能会将很多数据都设置到session中,导致session异常)。
-
演示
@Controller @SessionAttributes(value = "msg") public class OutputController { @RequestMapping("output1") public String output1(Model model){ model.addAttribute("msg","hello,Springmvc"); System.out.println(model.getClass()); return "output"; } }
3、使用@ModelAttribute来获取请求中的数据
-
@ModelAttribute注解用于将方法的参数或者方法的返回值绑定到指定的模型属性上,并返回给web视图。首先来介绍一个业务场景,来帮助大家做理解,在实际工作中,有些时候我们在修改数据的时候可能只需要修改其中几个字段,而不是全部的属性字段都获取,那么当提交属性的时候,从form表单中获取的数据就有可能只包含了部分属性,此时再向数据库更新的时候,肯定会丢失属性,因为对象的封装是springmvc自动帮我们new的,所以此时需要先将从数据库获取的对象保存下来,当提交的时候不是new新的对象,而是在原来的对象上进行属性覆盖,此时就需要使用@ModelAttribute注解。
-
演示
User.java
package com.mashibing.bean; public class User { private Integer id; private String name; private String password; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } }
UserController.java
package com.mashibing.controller; import com.mashibing.bean.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class UserController { Object o1 = null; Object o2 = null; Object o3 = null; @RequestMapping("update") public String update(@ModelAttribute("user") User user,Model model){ System.out.println(user); o2 = model; //可以看到所有的model都是同一个对象 System.out.println(o1==o2); //可以看到存储的user对象也是同一个 System.out.println(user == o3); return "output"; } @ModelAttribute public void MyModelAttribute(Model model){ o1 = model; User user = new User(); user.setId(1); user.setName("张三"); user.setAge(12); user.setPassword("123"); model.addAttribute("user",user); System.out.println("modelAttribute:"+user); o3 = user; } }
jsp
<%-- To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <form action="update" method="post"> <input type="hidden" value="1" name="id"> 姓名:张三<br> 密码:<input type="text" name="password"><br> 年龄:<input type="text" name="age"><br> <input type="submit" value="提交"> </form> </body> </html>
- 原理分析: @ModelAttribute使用在方法上时,会优先加载该方法,该方法创建user方法,并进行属性赋值,完成后将user对象传入model中(设置id=user),加载完成后。在方法参数加@ModelAttribute注解,将model中的对象传给参数(根据id获取)。当在jsp页面赋值的时候,实际上是拿被@ModelAttribute注解方法中的user对象进行属性覆盖。该对象地址没有改变,只是对象的属性改变了。简单来说就是MyModelAttribute()方法优先加载,获取到user对象,并将对象传给update()方法,在jsp页面输入其实就是在覆盖user的属性值。
-
其实在使用的时候可以简化写法,也就是说,在方法的参数上不加@ModelAttribute也不会有问题,但是参数User的属性user必须和 model.addAttribute(“user”,user);中设置的属性一致。
@RequestMapping("update") public String update(User user,Model model){ System.out.println(user); o2 = model; //可以看到所有的model都是同一个对象 System.out.println(o1==o2); //可以看到存储的user对象也是同一个 System.out.println(user == o3); return "output"; }
-
如果添加的@ModelAttribute(“”)属性的值不对,那么也是获取不到值的。同时可以添加@SessionAttributes属性,但是注意,如果没有设置值的话,会报错
@Controller @SessionAttributes("u") public class UserController { Object o1 = null; Object o2 = null; Object o3 = null; @RequestMapping("update") public String update(@ModelAttribute("u") User user,Model model){ System.out.println(user); o2 = model; //可以看到所有的model都是同一个对象 System.out.println(o1==o2); //可以看到存储的user对象也是同一个 System.out.println(user == o3); return "output"; } @ModelAttribute public void MyModelAttribute(Model model){ o1 = model; User user = new User(); user.setId(1); user.setName("张三"); user.setAge(12); user.setPassword("123"); model.addAttribute("user",user); System.out.println("modelAttribute:"+user); o3 = user; } }
- model.addAttribute(“user”,user);也就是说存入将user存入request中,并设置id=user,当时在获取的时候@ModelAttribute(“u”)和SessionAttributes(“u”)获取的id都是u,因此获取不到,将会报错。
-
总结:通过刚刚的给参数赋值,大家应该能够发现,当给方法中的参数设置值的时候,如果添加了@ModelAttribute注解,那么在查找值的时候,是遵循以下方式:
- 方法的参数使用参数的类型首字母小写,或者使用@ModelAttribute("")的值
- 先看之前是否在model中设置过该属性值,如果设置过就直接获取
- 看@SessionAttributes注解标注类中的方法是否给session中赋值,如果有的话,也是直接获取,没有报异常
4、使用forward实现页面转发
-
在发送请求的时候,可以通过forward:来实现转发的功能:
-
演示:两种使用方法
package com.mashibing.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ForWardController { /** * 当使用转发的时候可以添加前缀forward:index.jsp,此时是不会经过视图解析器的,所以要添加完整的名称 * * forward:也可以由一个请求跳转到另外一个请求 * * @return */ @RequestMapping("/forward01") public String forward(){ System.out.println("1"); return "forward:/index.jsp"; } //实现多重转发 @RequestMapping("/forward02") public String forward2(){ System.out.println("2"); return "forward:/forward01"; } }
5、使用redirect来实现重定向
-
在发送请求的时候,可以通过redirect:来实现重定向的功能
-
演示:两种使用方法
@Controller public class RedirectController { /** * redirect :重定向的路径 * 相当于 response.sendRedirect("index.jsp") * 跟视图解析器无关 * @return */ @RequestMapping("redirect") public String redirect(){ System.out.println("redirect"); return "redirect:/index.jsp"; } //双重 @RequestMapping("/redirect2") public String redirect2(){ System.out.println("redirect2"); return "redirect:/redirect"; } }
6、转发和冲定向的区别
-
转发:由服务器的页面进行跳转,不需要客户端重新发送请求:
-
地址栏的请求不会发生变化,显示的还是第一次请求的地址
-
请求的次数,有且仅有一次请求
-
请求域中的数据不会丢失
-
根目录:localhost:8080/项目地址/,包含了项目的访问地址
-
-
重定向:在浏览器端进行页面的跳转,需要发送两次请求(第一次是人为的,第二次是自动的)
-
地址栏的地址发生变化,显示最新发送请求的地址
-
请求次数:2次
-
请求域中的数据会丢失,因为是不同的请求
-
根目录:localhost:8080/ 不包含项目的名称
-
- 对比
区别 | 转发forward() | 重定向sendRedirect() |
---|---|---|
根目录 | 包含项目访问地址 | 没有项目访问地址 |
地址栏 | 不会发生变化 | 会发生变化 |
哪里跳转 | 服务器端进行的跳转 | 浏览器端进行的跳转 |
请求域中数据 | 不会丢失 | 会丢失 |