目录
4、使用Model,Map,ModelMap传输数据到页面(回滚参数值)
7、使用@ModelAttribute(模型属性)来获取请求中的数据
4、使用Model,Map,ModelMap传输数据到页面(回滚参数值)
在刚开始的helloworld项目中,我们传递了参数回到我们页面,但是后续的操作都只是接受用户的请求,那么在SpringMVC中除了可以使用原生servlet的对象传递数据之外,还有什么其他的方式呢?
可以在方法的参数上传入Model,ModelMap,Map类型,此时都能够将数据传送回页面
导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
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>
===============================================================================
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
//控制器映射
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.mashibing">
</context:component-scan>
<bean class="
org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp"></property> //后缀
<property name="prefix" value="/WEB-INF/page/"></property> //前缀
</bean>
</beans>
java文件:OutputController(输出控制器)
@Controller
public class OutputController {
@RequestMapping请求路径("/output")
public String output(Map<String,String> map){
map.put("msg","hello,springmvc ");
return "success";
}
}
success.jsp
设置服务器:
java文件:OutputController(输出控制器)
@Controller
public class OutputController {
@RequestMapping请求路径("/output")
public String output(Map<String,String> map){
map.put("msg","hello,springmvc ");
return "success";
}
================================== 添加 =======================================
@RequestMapping("/output2")
public String output2(Model model){
model.addAttribute("msg","hello,springmvc");
return "success";
}
@RequestMapping("/output3")
public String output3(ModelMap modelMap){
modelMap.addAttribute("msg","hello,springmvc");
return "success";
}
===============================================================================
}
下面三个结果都一样:
在向页面回显数据的时候,可以在方法的参数中显示的声明
Map:
ModelMap:
Model:
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
欢迎<br>
${msg}<br>
================================== 添加 =======================================
page:${pageScope.msg}<br> //当前页面范围
request:${requestScope.msg}<br> //请求范围
session:${sessionScope.msg}<br> //会话范围
application:${applicationScope.msg}<br> //应用范围
===============================================================================
</body>
</html>
下面三个结果都一样:
都可以将数据进行回显:回显之后数据是保存在哪个作用域中的?(使用范围)
page: 当前页面
request: 当前请求
session: 当前会话
application:当前应用
当使用上述参数传递数据的时候回把数据都放置到Request作用域
java文件:OutputController(输出控制器)
@Controller
public class OutputController {
@RequestMapping请求路径("/output")
public String output(Map<String,String> map){
map.put("msg","hello,springmvc ");
return "success";
}
@RequestMapping("/output2")
public String output2(Model model){
model.addAttribute("msg","hello,springmvc");
return "success";
}
@RequestMapping("/output3")
public String output3(ModelMap modelMap){
modelMap.addAttribute("msg","hello,springmvc");
return "success";
}
================================== 添加 =======================================
@RequestMapping请求路径("/output4")
public ModelAndView output4(){
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("msg","springmvc");
return mv;
===============================================================================
}
}
下面三个结果都一样:
当使用此方式进行设置之后,会发现所有的参数值都设置到了request作用域中,那么这三个对象是什么关系呢?
5、使用ModelAndView对象传输数据到页面
java文件:OutputController(输出控制器)
@Controller
================================== 添加 =======================================
@SessionAttributes会话属性(value = "username")
===============================================================================
public class OutputController {
@RequestMapping("/output")
public String output(Map<String,String> map){
map.put("msg","hello,springmvc ");
System.out.println(map.getClass());
return "success";
}
@RequestMapping("/output2")
public String output2(Model model){
model.addAttribute("msg","hello,springmvc");
System.out.println(model.getClass());
return "success";
}
@RequestMapping("/output3")
public String output3(ModelMap modelMap){
modelMap.addAttribute("msg","hello,springmvc");
System.out.println(modelMap.getClass());
return "success";
}
@RequestMapping("/output4")
public ModelAndView output4(){
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("msg","springmvc");
return mv;
}
================================== 添加 =======================================
@RequestMapping("/testSession")
public String testSession(Model model){
model.addAttribute("username","zhangsan");
return "success"; //成功
}
===============================================================================
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
欢迎<br>
${msg}<br>
page:${pageScope.msg}<br> //当前页面范围
request:${requestScope.msg}<br> //请求范围
session:${sessionScope.msg}<br> //会话范围
application:${applicationScope.msg}<br> //应用范围
================================== 添加 =======================================
<hr>
session:${sessionScope.username}<br> //会话范围
request:${requestScope.username}<br> //请求范围
===============================================================================
</body>
</html>
发现当使用modelAndView对象的时候,返回值的类型也是此对象,可以将要跳转的页面设置成view的名称,来完成跳转的功能,同时数据也是放到request作用中
6、使用session传输数据到页面
@SessionAttribute(会话属性):此注解可以表示,当向request作用域设置数据的时候同时也要向session中保存一份,此注解有两个参数,一个value(表示将哪些值设置到session中),另外一个type(表示按照类型来设置数据,一般不用,因为有可能会将很多数据都设置到session中,导致session异常)
java文件:OutputController(输出控制器)
@Controller
================================ 添加"msg" ====================================
@SessionAttributes会话属性(value = {"username","msg"}) //测试两个谁的优先级高
===============================================================================
public class OutputController {
@RequestMapping("/output")
public String output(Map<String,String> map){
map.put("msg","hello,output"); //改output
System.out.println(map.getClass());
return "success";
}
@RequestMapping("/output2")
public String output2(Model model){
model.addAttribute("msg","hello,output2"); //改output2
System.out.println(model.getClass());
return "success";
}
@RequestMapping("/output3")
public String output3(ModelMap modelMap){
modelMap.addAttribute("msg","hello,output3"); //改output3
System.out.println(modelMap.getClass());
return "success";
}
@RequestMapping("/output4")
public ModelAndView output4(){
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("msg","output4"); //改output4
return mv;
}
@RequestMapping("/testSession")
public String testSession(Model model){
model.addAttribute("username","zhangsan");
return "success"; //成功
}
}
当需要向session(会话)中设置数据的时候,可以在当前Controller(控制器)上添加@SessionAttributes(会话属性)
此注解表示,每次在向request作用中设置属性值的时候,顺带着向session(会话)中保存一份
value和type(类型)都写上之后表示session(会话)中可以存储名字为value值的参数以及类型为Integer的参数
value和type可以分开单独使用,但是尽量不要一起使用,因为type类型会把所有符合类型的数据都设置到session(会话)中,导致session(会话)异常
推荐这样写:
java文件:OutputController(输出控制器)
@Controller
================================== 修改 =======================================
@SessionAttributes(types = String.class) //把所有符合类型的数据都设置到session(会话)中
===============================================================================
public class OutputController {
@RequestMapping("/output")
public String output(Map<String,String> map){
map.put("msg","hello,output"); //改output
System.out.println(map.getClass());
return "success";
}
@RequestMapping("/output2")
public String output2(Model model){
model.addAttribute("msg","hello,output2"); //改output2
System.out.println(model.getClass());
return "success";
}
@RequestMapping("/output3")
public String output3(ModelMap modelMap){
modelMap.addAttribute("msg","hello,output3"); //改output3
System.out.println(modelMap.getClass());
return "success";
}
@RequestMapping("/output4")
public ModelAndView output4(){
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("msg","output4"); //改output4
return mv;
}
@RequestMapping("/testSession")
public String testSession(Model model){
model.addAttribute("username","zhangsan");
return "success"; //成功
}
}
7、使用@ModelAttribute(模型属性)来获取请求中的数据
java文件:User(用户)
public class User {
private Integer id; //id
private String name; //名字
private String password; //密码
private Integer age; //年龄
public User() {}
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 +
'}';
}
}
update更新.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
================================== 添加 =======================================
<%
pageContext.setAttribute("ctp",request.getContextPath());
%>
<body>
<form action="${ctp}/update">
<input type="hidden" value="1" name="id"><br>
name:张三<br> //直接显示名字,不显示输入框
password:<input type="password" name="password"><br>
age:<input type="text" name="age"><br>
<input type="submit" value="更新"><br>
===============================================================================
</form>
</body>
</html>
java文件:UserController(用户控制器)
@Controller
public class UserController {
@RequestMapping("/update")
public String update(User user){
System.out.println(user);
return "success";
}
}
//里面张三直接显示
跳转到:
//idea里面张三
没显示
(先查询,再取出)
====================== 解决方式 ===========================
java文件:UserController(用户控制器)
@Controller
public class UserController {
============ 添加@ModelAttribute("user"),就是每次执行都创建User =============
@RequestMapping("/update")
public String update(@ModelAttribute("user") User user){
System.out.println(user);
return "success";
}
================================== 添加 =======================================
@ModelAttribute
public void testModelAttribute(Model model){
User user = new User();
user.setId(1);
user.setName("李四");
user.setAge(11);
user.setPassword("1234");
model.addAttribute("user",user); //增加属性
===============================================================================
}
}
跳转到:
//成功
//比较地址空间
如果是true两个就相同:
//改成2都一样?
结论:先执行第二个属性值方法,再执行第一个方法
//一样
java文件:UserController(用户控制器)
@Controller
public class UserController {
Object o1 = null;
Object o2 = null;
Model m1 = null;
@RequestMapping("/update")
public String update(@ModelAttribute("user2") User user,Model model){
System.out.println("update--------------------");
o2 = user;
System.out.println(user);
System.out.println(o1 == o2);
System.out.println(m1 == model);
return "success";
}
@ModelAttribute
public void testModelAttribute(Model model){
System.out.println("testModelAttribute---------------+");
User user = new User();
user.setId(1);
user.setName("李四");
user.setAge(11);
user.setPassword("1234");
model.addAttribute("user2",user); //增加属性
o1 = user;
m1 = model;
}
================================== 添加 =======================================
@ModelAttribute
public void testModelAttribute2(Model model){
System.out.println("testModelAttribute2---------------+");
}
===============================================================================
}
//从下往上执行
java文件:UserController(用户控制器)
@Controller
public class UserController {
Object o1 = null;
Object o2 = null;
Model m1 = null;
@RequestMapping("/update")
public String update(@ModelAttribute("user2") User user,Model model){
System.out.println("update--------------------");
o2 = user;
System.out.println(user);
System.out.println(o1 == o2);
System.out.println(m1 == model);
return "success";
}
================================== 添加 =======================================
@RequestMapping("/update2")
public String update2(){ //这里不添加@ModelAttribute(模型属性)
System.out.println("update2----------");
return "success";
}
===============================================================================
@ModelAttribute
public void testModelAttribute(Model model){
System.out.println("testModelAttribute---------------+");
User user = new User();
user.setId(1);
user.setName("李四");
user.setAge(11);
user.setPassword("1234");
model.addAttribute("user2",user); //增加属性
o1 = user;
m1 = model;
}
@ModelAttribute
public void testModelAttribute2(Model model){
System.out.println("testModelAttribute2---------------+");
}
}
其实在使用的时候可以简化写法,也就是说,在方法的参数上不加@ModelAttribute也不会有问题
//那么问题来了,其中第一二个方法会执行吗?
跳转到:
//成功
//说白了就是刚创建的方法与第一个方法不相同
========================================================
如果添加的@ModelAttribute模型属性(" ")属性的值不对,那么也是获取不到值的。同时可以添加@SessionAttributes会话属性,但是注意,如果没有设置值的话,会报错
跳转到://报错:没有该属性值
@SessionAttributes(会话属性)要注意,在使用的时候如果没有对应的值,可能会报异常
@ModelAttribute(模型属性)注解用于将方法的参数或者方法的返回值绑定到指定的模型属性上,并返回给web视图。首先来介绍一个业务场景,来帮助大家做理解,在实际工作中,有些时候我们在修改数据的时候可能只需要修改其中几个字段,而不是全部的属性字段都获取,那么当提交属性的时候,从form表单中获取的数据就有可能只包含了部分属性,此时再向数据库更新的时候,肯定会丢失属性,因为对象的封装是springmvc自动帮我们new的,所以此时需要先将从数据库获取的对象保存下来,当提交的时候不是new新的对象,而是在原来的对象上进行属性覆盖,此时就需要使用@ModelAttribute(模型属性)
注意:@ModelAttribute除了可以使用设置值到model中之外,还可以利用返回值
//李四拿到这个值
在使用@ModelAttribute的时候,需要注意:
1、如果参数中的注解没有写名字的话,默认是用参数名称的首字母小写来匹配
2、如果有值,直接返回,如果没有值,会去session中进行查找,也就是说会判断当前类上是否添加过@SessionAttributes(会话属性)
推荐:注解中最好添加参数,来作为标识,进行对象属性的赋值
总结:通过刚刚的给参数赋值,大家应该能够发现,当给方法中的参数设置值的时候,如果添加了@ModelAttribute(模型属性),那么在查找值的时候,是遵循以下方式:
1、方法的参数使用参数的类型首字母小写,或者使用@ModelAttribute("")的值
2、先看之前是否在model中设置过该属性值,如果设置过就直接获取
3、看@SessionAttributes(会话属性)注解标注类中的方法是否给session中赋值,如果有的话,也是直接获取,没有报异常
8、使用forward(转发)实现页面转发
在发送请求的时候,可以通过forward(转发):来实现转发的功能:
index.jsp
java文件:ForWardController(转发控制器)
@Controller
public class ForWardController {
@RequestMapping("/forward")
public String forward(){
System.out.println("forward");
return "forward:/index.jsp";
}
@RequestMapping("/forward2")
public String forward2(){
System.out.println("forward2");
return "forward:/forward";
//"forward:/forward"意思是:返回到public String forward(){..}的方法里面
(转发的除了可以转发回到页面之外,还可以转发到其他请求中)
}
}
在使用转发的时候前面要添加forward:前缀,同时通过写的字符串能够看到forward请求,而不经过视图处理器
9、使用redirect(重定向)来实现重定向
java文件:RedirectController(重定向控制器)
@Controller
public class RedirectController {
//使用重定向的时候需要注意,添加redirect:前缀
//重定向操作也不会经过视图处理器(视图解析器?)
@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";
}
}
在javaweb的时候大家应该都接触过重定向和转发的区别:
转发:
由服务器的页面进行跳转,不需要客户端重新发送请求,特点如下:
1、地址栏的请求不会发生变化,显示的还是第一次请求的地址
2、请求的次数,有且仅有一次请求
3、请求域中的数据不会丢失
4、根目录:localhost:8080/项目地址/,包含了项目的访问地址
重定向:
在浏览器端进行页面的跳转,需要发送两次请求(第一次是人为的,第二次是自动的)
特点如下:
1、地址栏的地址发生变化,显示最新发送请求的地址
2、请求次数:2次
3、请求域中的数据会丢失,因为是不同的请求
4、根目录:localhost:8080/ 不包含项目的名称
对比:
区别 | 转发forward() | 重定向sendRedirect() |
根目录 | 包含项目访问地址 | 没有项目访问地址 |
地址栏 | 不会发生变化 | 会发生变化 |
哪里跳转 | 服务器端进行的跳转 | 浏览器端进行的跳转 |
请求域中数据 | 不会丢失 | 会丢失 |
10、静态资源的访问
java文件:StaticController(静态控制器)
@Controller
public class StaticController {
@RequestMapping("/static")
public String testStatic(){
System.out.println("static");
return "forwrd:/index.jsp";
}
}
添加图片:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
================================== 添加 =======================================
<%
pageContext.setAttribute("ctp",request.getContextPath());
%>
<body>
666666666<br>
<img src="${ctp}/images/timg.jpg">
===============================================================================
</body>
</html>
//(图片在imagse文件夹下)
此时大家发现我们请求的图片根本访问不到,根据查看发现路径是没有问题的,那么为什么会找不到静态资源呢?
大家发现此时是找不到对应的mapping(映射),此时是因为DispatcherServlet(前端控制器)会拦截所有的请求,而此时我们没有对应图片的请求处理方法,所以此时报错了,想要解决的话非常简单,只需要添加一个配置即可
=================== 解决图片(静态资源)问题 ====================
springmvc.xml
上面第一行代码:默认-控制器-处理
此配置表示 我们自己配置的请求由controller(控制器)来处理,但是不能请求的处理交由tomcat来处理
静态资源可以访问,但是动态请求无法访问
上面第二行代码:注解-被动
第二个也可以简化这样写:<mvc:annotation-driven>
但是加上此配置之后,大家又发现此时除了静态资源无法访问之外,我们正常的请求也无法获取了,因此还需要再添加另外的配置:
保证静态资源和动态请求都能够访问
使用默认的servlet-handler来处理静态资源,原来请求不到原因在于所有的请求都交由DispatcherServlet来处理
但是DispatcherServlet中没有对应静态资源的处理逻辑,所以访问不到,添加默认之后就可以了,但是会发现此时动态请求无法完成
所以需要配合另外一个标签类配合使用
//也可以在web.xml内添加:
//
===============================================================
如果出现这种情况,就刷新整个项目试试(不用重启idea):
============================================================