SpringMVC 的响应处理

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

一、视图解析器

上篇文章我们作为示例的项目中,新建的jsp页面直接放在了web文件夹下,这样就可以通过URL地址,直接对jsp文件进行访问,这样有利有弊.
好处是不用经过控制器直接对jsp进行访问,比如一些简单静态页面,我们就不用通过控制器处理,可以直接访问。
坏处也是因为可以直接访问,对于一些非静态数据的页面,如果不经过控制器,直接访问页面,数据就无法正确显示,造成访问问题。

springmvc推荐我们将jsp访问WEB-INFO中,这样外部就无法直接进行访问,只能通过控制器进行重定向或者转发才能访问到jsp页面。

如果实际项目中如果需要用到springmvc 并且有jsp页面,我们可以灵活应用,将一些非静态不能直接访问的jsp放到WEB-INFO中,将一些静态可以直接访问的页面可以直接放在web下,这样可以直接进行访问。

image.png

如上图可以看到,Controller接收到请求之后,通过转发的方式,从根目录依次安装目录结构定位到最终页面,响应到浏览器。

但是如果后面WEB-INFO下的目录结构很多,那在return jsp路径的时候就比较繁琐了

1、springmvc内置视图解析器

1-1、在spring-mvc.xml配置视图解析器,设置前缀后缀

首先在spring-mvc.xml中注入内置的视图解析器

我们需要写上我们的前缀,也就是“WEB-INF/views/”--最后这个“/”需要加上,要不Controller中返回的页面前面就都需要加“/”了。 后缀就设为.jsp

这样只有前缀为“WEB-INF/views/” 后缀为“.jsp”才会经过视图解析器。 image.png

1-3、修改Controller的返回

可以看到我们返回,只需要写上index就可以了,这样就简便了很多 image.png

1-4、测试

image.png

2、视图控制器

上面提到springmvc推荐我们将jsp页面放到WEB-INF中,那如果我们一个静态页面,也放到WEB-INF中,就需要在Controller中添加一个方法,然后进行跳转,如下:

2-1、新建一个测试页面

image.png

2-2、编写跳转方法

image.png 这样我们就可以跳转到test页面了,但是这样太繁琐了,只要添加一个不需要经过Controller的页面都必须在Controller添加一个方法。下面我们就来介绍视图控制器,这样就不用在Controller中写方法了。

2-3、视图控制器的添加

在spring-mvc.xml中添加视图控制器

image.png 通过以上配置,我们就可以通过“/”来跳转到index.jsp,然后通过“/test”访问到test.jsp页面了,其中path可以随便定义,可以不和视图jsp名称一致,但是建议为一致,方便后期查找。

2-3-1、删除Controller的方法,进行测试

image.png

image.png

二、将数据传输到页面

在刚开始的helloworld项目中,我们传递了参数回到我们页面,但是后续的操作都只是接受用户的请求,那么在SpringMVC中除了可以使用原生servlet的对象传递数据之外,还有什么其他的方式呢?

2-1、使用Model/ModelMap/Map进行数据传输

我们可以使用tomcat带的jar包,使用HttpServletRequest传递参数,也可以在方法的参数上传入Model,ModelMap,Map类型,此时都能够将数据传送回页面

2-1-1、使用HttpServletRequest传参

image.png

2-1-2、使用Model传参

image.png

2-1-3、使用ModelMap传参

image.png

2-1-4、使用Map传参

image.png

通过以上测试,我们可以发现使用4中方式设置参数,在页面上都可以用requestScope获得参数,那4中方式之间一定有什么联系,我们可以看下他们的类图,如下:

image.png

可以看到Map是最顶层,然后最终实际他们使用的是BindingAwareModelMap,下面我们可以获得类的class查看一下。

image.png

因此这三类使用的都是一个,用哪种都是可以的。

2-2、使用ModelAndView对象传输数据到页面

ModelAndView顾名思义,就是讲model和View进行同步处理。既可以传参,也可以跳转视图。

image.png

2-3、使用session向页面传输数据

2-3-1、通过servlet api的方式写session

用session传输数据、页面上可以使用如下代码来获取数据

sessionType:${sessionScope.type}
复制代码

2-3-1-1、通过参数绑定的方式获取servlet api

image.png

2-3-1-2、通过自动注入的方式去获取servlet api

image.png 综上两种方案,建议使用第二种方案,将session 注入的类中,这样类中的所有方法就都可以使用session了。

2-3-2、使用springmv提供的注解方式读写session

2-3-2-1、@SessionAttributes

@SessionAttributes设置在类上面,写入session
1、从model中获取指定的属性写入session中
2、底层会从model中去找一个叫做type的属性
3、找到了会将type设置一份到session中
4、这种方式是依赖model的
5、当前控制器下所有的处理方法 都会将model指定的属性写入session

image.png 如上图,我们给类上加@SessionAttributes("type"),然后在方法内用Model设置了type的值,这样底层就会去model中找type属性,找到之后会复制一份到session中。

2-3-2-2、@SessionAttribute

用在参数上面的,负责读取session

默认指定的属性是必须要存在的,如果不存在则会报错,可以设置required =false 不需要必须存在,不存在默认绑定null

第一次启动,然后从session中获取session中的type是null image.png

然后通过访问之前的model方法,再次访问sessionAttr就可以获得session 中的值了。如下:

image.png 上面通过SessionAttribute获取type值的时候,实际上同时执行了model.addAttribute("type",type);

在类上使用@SessionAttributes注解的情况下并且设置的key一直,比如上面都设置的是type,model是互通的,session可以通过model中去获取写入指定的属性,model也会从session中自动写入指定的属性

在实际开发过程中建议使用servlet api的方式用@AutoWrite 自动注入HttpSession的方式来处理,因为使用springmvc 的@SessionAttributes方式model和session互通,可能有时候就会有未知的问题。

2-4、使用@ModelAttribute来获取请求中的数据

2-4-1、写在方法上面,执行方法前,先调用

@ModelAttribute的方法会在当前处理器中所有的处理方法之前调用

image.png

2-4-2、@ModelAttribute使用场景

1.通过@ModelAttribute来给全局变量赋值(不推荐,不是线程安全的)

image.png

  1. 当我们调用执行全字段的更新数据库操作时,假如提供给用户的修改字段只有部分几个,这个时候就会造成其他字段更新丢失:

解决:
1.自己定制update语句, 只更新指定的那些字段 2.如果无法定制sql语句, 可以在更新之前进行查询, 怎么在更新之前查询?只能在springmvc 绑定请求参数之前查询, 利用@ModelAttribute就可以在参数绑定之前查询, 但是怎么将查询出来的对象和参数的对象进行合并? springmvc具有该特性, 会将model中和参数名相同的属性拿出来进行合并,将参数中的新自动进行覆盖,没有的字段进行保留。这样就可以解决这个问题。

2.写在参数上面

可以省略,加上则会从model中获取一个指定的属性和参数进行合并,因为model和sessionAttribute具有共通的特性,所以如果session中有对应的属性也会进行合并

1 @ModelAttribute("user")

@ModelAttribute注解用于将方法的参数或者方法的返回值绑定到指定的模型属性上,并返回给web视图。首先来介绍一个业务场景,来帮助大家做理解,在实际工作中,有些时候我们在修改数据的时候可能只需要修改其中几个字段,而不是全部的属性字段都获取,那么当提交属性的时候,从form表单中获取的数据就有可能只包含了部分属性,此时再向数据库更新的时候,肯定会丢失属性,因为对象的封装是springmvc自动帮我们new的,所以此时需要先将从数据库获取的对象保存下来,当提交的时候不是new新的对象,而是在原来的对象上进行属性覆盖,此时就需要使用@ModelAttribute注解

总结:

通过刚刚的给参数赋值,大家应该能够发现,当给方法中的参数设置值的时候,如果添加了@ModelAttribute注解,那么在查找值的时候,是遵循以下方式:

1、方法的参数使用参数的类型首字母小写,或者使用@ModelAttribute("")的值

2、先看之前是否在model中设置过该属性值,如果设置过就直接获取

3、看@SessionAttributes注解标注类中的方法是否给session中赋值,如果有的话,也是直接获取,没有报异常

2-5、我们上通过三种方式获得了session,那是否会有线程安全问题呢

  1. 通过参数绑定的方式

是线程安全的,因为参数绑定变量时方法级别,所以每次请求方法都会创建一个新的私有变量。

2.通过自动注入的方式@AutoWired

是线程安全的,spring底层通过自动注入 将 servlet api 封装到ThreadLocal(是绑定在线程上面的)里面.

3.通过@ModelAttribute的方式

不是线程安全的,控制器是单例的,并且变量声明在类的级别中(共享变量)。

三、springmvc中的转发和重定向

我们在跳转视图的时候,之前有提到过,默认是forward,也可以指定为redirect,如 return "redirect:index" 这样就重定向到了index视图。

3-1、转发

由服务器的页面进行跳转,不需要客户端重新发送请求:特点如下: 1、地址栏的请求不会发生变化,显示的还是第一次请求的地址

2、请求的次数,有且仅有一次请求

3、请求域中的数据不会丢失

4、根目录:localhost:8080/项目地址/,包含了项目的访问地址

1648390908(1).jpg

3-1-1、测试

一般情况下面是默认使用forward的方式。
在之前配置了视图解析器,前缀是WEB-INF/views 后缀是.jsp,就会到视图解析器中,然后指向views中的视图,但是如果我们跳转WEB-INF外的视图,就需要在return 中添加forward了,这样视图解析器就无法进行监控了。地址栏同样也未发生变化 image.png

3-2、重定向

在浏览器端进行页面的跳转,需要发送两次请求(第一次是人为的,第二次是自动的)特点如下:

1、地址栏的地址发生变化,显示最新发送请求的地址

2、请求次数:2次(第二次请求一定是get方式)

3、请求域中的数据会丢失,因为是不同的请求

4、根目录:localhost:8080/ 不包含项目的名称

1648391159.jpg

3-2-1、测试

重定向不会走视图

image.png 可以看到地址栏已经发生变化,重定向成功,之前提到重定向会丢失项目名,这个地方未丢失项目名,是由于我们在login.jsp 前面添加了“/”,这样springmvc就会在底层自动加上项目名,因此在servlet中一定要加上项目名

如果我要重定向到WEB-INF/views下的页面是否可行呢?答案是否定的,因为我们WEB-INF/下的视图是受限访问的,只能通过控制器跳转,而重定向即使设置return "redirect:/WEB-INF/views/index.jsp"也不可行,这样相当于让浏览器自己对这个连接进行访问,显然springmvc会进行阻止访问。

转发和重定向对比

区别 转发:forward() 重定向:sendRedirect()
根目录 /(在springmvc中无论转发还是重定向都会包含项目名) 包含项目访问地址 没有项目访问地址
地址栏 不会发生变化 会发生变化
哪里跳转 服务器端进行的跳转 浏览器端进行的跳转
请求域中数据 不会丢失 会丢失

猜你喜欢

转载自juejin.im/post/7082950839454662669