SpringMVC day02
1 数据传输
从Controller跳转到View,通常是需要传输数据的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rtdncbDp-1630975475698)(SpringMVC day02.assets/image-20210623224302104.png)]
1.1 请求转发中传递数据
1.1.1 Map、Model和ModelMap
SpringMVC中如果需要在一次请求中传递数据,可以使用Map、Model和ModelMap代替request作用域。
@RequestMapping("/data")
public class DataTransferController {
@RequestMapping("/map")
public String map(Map map){
System.out.println("map = [" + map.getClass() + "]");
map.put("map_name", "xiaohei");
map.put("map_age", 18);
map.put("map_u", new User(1, "xiaobai", "123456"));
return "forward:/index.jsp";
}
@RequestMapping("/model")
public String model(Model model) {
System.out.println("model = [" + model.getClass()+ "]");
model.addAttribute("model_name", "xiaohei");
model.addAttribute("model_age", 18);
model.addAttribute("model_u", new User(1, "xiaobai", "123456"));
return "forward:/index.jsp";
}
@RequestMapping("/modelMap")
public String modelMap(ModelMap map){
System.out.println("modelMap = [" + map.getClass() + "]");
map.put("modelMap_name", "xiaohei");
map.put("modelMap_age", 18);
map.addAttribute("modelMap_u", new User(1, "xiaobai", "123456"));
return "forward:/index.jsp";
}
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
map代替request作用域 <br>
${requestScope.map_name} <br>
${requestScope.map_age} <br>
${requestScope.map_u}
<hr>
model代替request作用域 <br>
${requestScope.model_name} <br>
${requestScope.model_age} <br>
${requestScope.model_u}
<hr>
modelMap代替request作用域 <br>
${requestScope.modelMap_name} <br>
${requestScope.modelMap_age} <br>
${requestScope.modelMap_u}
<hr>
</body>
</html>
SpringMVC会自动为3种类型的参数注入对象,且都是同一类型的对象,3种方式本质上是同一种方式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uae042Hd-1630975475701)(SpringMVC day02.assets/image-20210624194955183.png)]
1.1.2 ModelAndView[了解]
ModelAndView本身封装了一个ModelMap,它也可以用于传输数据。
@RequestMapping("/mav")
public ModelAndView modelAndView(){
ModelAndView mav = new ModelAndView("forward:/index.jsp");
mav.addObject("mav_name", "xiaohei");
mav.addObject("mav_age", 18);
mav.addObject("mav_-u", new User(1, "xiaobai", "123456"));
return mav;
}
1.1.3 ModelAttribute注解[了解]
可以使用ModelAttribute注解描述方法形参,ModelAttribute描述的形参自动添加到Model中
@RequestMapping("modelAttribute")
public String modelAttribute(@ModelAttribute("name") String username,@ModelAttribute("age") Integer age,@ModelAttribute("u") User user){
System.out.println("username = " + username + ", age = " + age + ", user = " + user);
return "forward:/index.jsp";
}
index.jsp
modelAttribute代替request作用域 <br>
${requestScope.name} <br>
${requestScope.age} <br>
${requestScope.u} <br>
注意:
- 请求中的简单类型参数名要和ModelAttribute保持一致
- 数据保存在Model中以ModelAttribute注解值为数据名,而非形参名。上例中可以通过
name
而不是username
从model中获取数据。
1.2 重定向中传递数据
1.2.1 Map、Model和ModelMap[了解]
Map、Model和ModelMap中保存的简单类型数据在重定向时会直接拼接在地址后
@RequestMapping("redirectPrimitive")
public String redirect(Map map, Model model, ModelMap modelMap) {
map.put("name", "xiaohei");
model.addAttribute("age", 18);
//复杂类型数据无法拼接在地址栏
modelMap.addAttribute("u", new User(1, "xiaohei", "123456"));
return "redirect:/data/redirectValue.do";
}
地址栏地址变成:http://localhost:8989/springmvc-day02/data/redirectValue.do?name=xiaohei&age=18
@RequestMapping("redirectValue")
public String redirectValue(String name,Integer age,Model model){
System.out.println("name = " + name + ", age = " + age);
System.out.println("DataTransferController.redirectValue");
model.asMap().forEach((k,v)->{
System.out.println(k+" = " + v);
});
return "forward:/index.jsp";
}
注意:
- 复杂数据不能拼接在地址栏,也就不能传递
- 因为是直接拼接地址栏(明文显示),一般传递对安全性要求不高的数据
1.2.2 RedirectAttributes
RedirectAttributes也是Model的子类型,可以通过其独有的 addFlashAttribute
方法在重定向时传递复杂数据;传递的数据可以在重定向后的Controller服务方法的Model中获取
@RequestMapping("redirectObject")
public String redirectObject(RedirectAttributes model){
System.out.println("model.getClass() = " + model.getClass());
model.addFlashAttribute("redirect_name", "xiaohei");
model.addFlashAttribute("redirect_age", 18);
model.addFlashAttribute("redirect_u", new User(1, "xiaohei", "123456"));
return "redirect:/data/redirectValue.do";
}
@RequestMapping("redirectValue")
public String redirectValue(String name,Integer age,Model model){
System.out.println("name = " + name + ", age = " + age);
System.out.println("DataTransferController.redirectValue");
model.asMap().forEach((k,v)->{
System.out.println(k+" = " + v);
});
return "forward:/index.jsp";
}
注意:
-
RedirectAttributes继承Model,继承到了
addAttribute
方法,使用时要和addFlashAttributes
区分开来 -
RedirectAttributes底层使用session完成数据传递:
1.重定向前将redirectAttributes中的数据保存到session
2.重定向后从session中获取数据并保存到model中,然后从session中移除数据
1.3 Session作用域
1.3.1 HttpSession原生作用域
在SpringMVC中可以通过HttpSession对象操作session作用域。
@RequestMapping("session")
public String session(HttpSession session){
session.setAttribute("session_name","xiaohei");
session.setAttribute("session_age",18);
session.setAttribute("session_u", new User(1, "xiaohei", "123456"));
return "redirect:/index.jsp";
}
index.jsp
httpSession操作session作用域 <br>
${sessionScope.session_name} <br>
${sessionScope.session_age} <br>
${sessionScope.session_u}
<hr>
1.3.2 SessionAttributes注解[了解]
SessionAttributes注解用在类上面,将Model中的数据复制到Session作用域中。
//将Model中名为name和age,类型为User的数据复制到Session作用域
@SessionAttributes(value = {"name","age"},types = {User.class})
public class DataTransferController {
...
}
SessionAttributes添加到Session作用域的数据,在服务方法里可以直接从Model中获取到
@RequestMapping("getSessionAttributes1")
public String getSessionAttributes1(Model model){
model.asMap().forEach((k,v)->{
System.out.println(k+" = " + v);
});
return "redirect:/index.jsp";
}
SessionAttribute注解用在方法参数前,从Session作用域中获取数据赋值给参数。
@RequestMapping("getSessionAttributes2")
//SessionAttribute默认必须从session中获取到数据,获取不到抛异常;可以设置required=false不再要求必须获取到
public String getSessionAttributes2(@SessionAttribute(value = "name",required = false) String name,
@SessionAttribute(value="age",required = false)Integer age,
@SessionAttribute(value="u")User user){
System.out.println("name = " + name + ", age = " + age + ", user = " + user);
return "redirect:/index.jsp";
}
ModelAttribute注解也会从Session中获取SessionAttributes保存的数据,如果获取不到会抛异常。
@RequestMapping("getSessionAttributes3")
public String getSessionAttributes3(@ModelAttribute("name") String username,@ModelAttribute("age") Integer age,@ModelAttribute("u") User user){
System.out.println("username = " + username + ", age = " + age + ", user = " + user);
return "forward:/index.jsp";
}
注意:需要使用 SessionStatus清除SessionAttributes添加到session中的数据.
@RequestMapping("/clearSession")
public String clearSession(/*HttpSession session*/SessionStatus sessionStatus){
// session.removeAttribute("u");
sessionStatus.setComplete();
return "forward:/index.jsp";
}
2 ModelAttribute注解[了解]
ModelAttribute注解有2种用法:用在方法上和形参前。
2.1 用在方法上
ModelAttribute修饰的方法会自动的在每一个RequestMapping修饰的服务方法前执行。
- ModelAttribute修饰的方法可以定义和RequestMapping修饰方法相同的形参列表
- ModelAttribute修饰方法的返回值会自动添加到Model中(返回值类型也可以为void,则不做添加处理)
@Controller
@RequestMapping("/ma")
public class ModelAttributeController {
@ModelAttribute("u")
public User findUser(@RequestParam(value="id")Integer id, Model model){
model.addAttribute("id",id);
model.addAttribute("name", "xushy");
User user = new User(1, "xiaohei", "123456");
return user;
}
@RequestMapping("/method")
public String testMethod(Map map){
System.out.println(map.get("u"));//返回user对象
System.out.println(map.get("name"));//"xushy"
return "forward:/index.jsp";
}
}
2.2 用在形参前
ModelAttribute注解用在形参前还可以从Model中获取数据,赋值给方法形参。
@Controller
@RequestMapping("/ma")
public class ModelAttributeController {
@Autowired
private UserService userService;
@ModelAttribute("u")
public User findUser(@RequestParam(value="id")Integer id, Model model){
model.addAttribute("name", "xushy");
User user = userService.showUser(id);
return user;
}
@RequestMapping("/param")
public String testParam(@ModelAttribute("u") User user,@ModelAttribute("age")Integer age){
System.out.println("user = " + user + ", age = " + age);
return "forward:/index.jsp";
}
}
总结下ModelAttribute获取数据的方式以及先后顺序:
- 先从model中取同名数据
- 如果第1步获取不到,且SessionAttributes中保存了同名数据,则从session中获取,获取不到上抛异常
- 如果是简单类型参数,前2步获取数据则直接使用,未获取到则继续从请求中获取同名参数
如果是复杂的对象类型参数,无论前2步是否获取到对象,在这一步都会从请求中获取参数为属性赋值
3 文件的上传和下载
3.1 文件复制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zknDZAqx-1630975475702)(SpringMVC day02.assets/image-20210625091719397.png)]
上传和下载是通过网络完成的特殊的文件复制。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQFgYLzC-1630975475704)(SpringMVC day02.assets/image-20210625091604584.png)]
Spring提供了文件复制的工具类:FileCopyUtils
@Test
public void testCopy2() throws IOException {
InputStream in = new FileInputStream("D:/test.jpg");
OutputStream out = new FileOutputStream("E:/test.jpg");
FileCopyUtils.copy(in,out);
}
3.2 文件上传
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5fiALdYt-1630975475705)(SpringMVC day02.assets/image-20210625144851531.png)]
准备文件上传依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
页面:
<form action="${pageContext.request.contextPath}/file/upload.do" method="post" enctype="multipart/form-data">
文件:<input type="file" name="pic" id=""> <br>
文件描述: <textarea name="description" id="" cols="30" rows="10"></textarea><br>
<input type="submit" value="提交">
</form>
Controller:
@RequestMapping("/file")
public class UploadController {
@RequestMapping("upload")
public String upload(MultipartFile pic, String description, HttpServletRequest request) throws IOException {
System.out.println("pic = " + pic + ", description = " + description);
//获取 项目中file文件夹的绝对路径
String fileRealPath = request.getServletContext().getRealPath("/file/");
System.out.println("fileRealPath = " + fileRealPath);
//获取上传文件相关的输入流
InputStream in = pic.getInputStream();
//向file文件夹中输出文件
OutputStream out = new FileOutputStream(fileRealPath + pic.getOriginalFilename());
FileCopyUtils.copy(in,out);
return "redirect:/index.jsp";
}
}
springmvc-servlet.xml
<!-- 配置文件上传解析器
注意:这里的id必须固定,SpringMVC会根据此id找解析器。
-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!-- 设置文件上传大小限制-->
<property name="maxUploadSize" value="2097154"></property>
<!-- 设置编码方式-->
<property name="defaultEncoding" value="utf-8"/>
</bean>
3.3 文件下载
文件下载的思路:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-60x4Mo5e-1630975475706)(SpringMVC day02.assets/image-20210625145446594.png)]
页面:
<a href="${pageContext.request.contextPath }/file/download?fileName=test.txt">下载</a>
Controller:
@RequestMapping("/download")
public void download(String fileName, HttpServletRequest request,HttpServletResponse response) throws IOException {
//1.通过文件名从file目录中获取文件相关的输出流
String fileRealPath = request.getServletContext().getRealPath("/file/");
InputStream in = new FileInputStream(fileRealPath+fileName);
//2.设置响应格式和文件的名字
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(fileName,"utf-8"));
//3.将文件通过resp的输出流,输出到client浏览器
FileUtils.copyFile(in,response.getOutputStream());
}
新建一个项目,完成作业:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZZFXpas-1630975475707)(SpringMVC day02.assets/image-20210302221116365.png)]
4 拦截器
SpringMVC的拦截器和Servlet中的Filter类似,可以在请求到达Controller之前先经过拦截器,在Controller处理完请求后,会再次反向经过拦截器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNIdHUgZ-1630975475708)(SpringMVC day02.assets/image-20210625150212547.png)]
作用:
- 可以抽取Controller的共性代码,提高代码的复用性。
- 拦截器可以改变请求的原始流程,可以用于强制登录。
4.1 第1个Interceptor程序
开发步骤:
-
编码
public class LoginInterceptor implements HandlerInterceptor { @Override //请求到达Controller前执行,可用于登录校验 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //return true 放行请求 false 不放行 HttpSession session = httpServletRequest.getSession(); Object login = session.getAttribute("login"); if(login != null){ return true; }else { httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/login.jsp"); return false; } } @Override //Controller方法执行后,进入视图前:可修改视图的地址 public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override //最后执行,可用于释放资源,或者异常的记录 public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
-
配置
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login.do"/> <mvc:exclude-mapping path="/register.do"/> <bean class="com.baizhi.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
4.2 多个Interceptor的执行流程
(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
2. 配置
```xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login.do"/>
<mvc:exclude-mapping path="/register.do"/>
<bean class="com.baizhi.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4.2 多个Interceptor的执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cBanr1Am-1630975475709)(SpringMVC day02.assets/55ab23425a904dfe9ff076c60a171e1e~tplv-k3u1fbpfcp-watermark.image)]