Spring Web MVC

Spring Web MVC

Spring Web MVC is the original web framework built on the Servlet API and included in the Spring Framework from the very beginning. The formal name "Spring Web MVC" comes from the name of its source module spring-webmvc but it is more commonly known as " Spring MVC".
Parallel to Spring Web MVC, Spring Framework 5.0 introduced a reactive stack, web framework whose name Spring WebFlux is also based on its source module spring-webflux. This section covers Spring Web MVC. The next section covers Spring WebFlux.
For baseline information and compatibility with Servlet container and Java EE version ranges please visit the Spring Framework Wiki.

Spring MVC入门

入门教程可以参考: https://course.tianmaying.com/spring-mvc

Spring MVC是什么

Spring Web MVC(下文简称Spring MVC)和Struts2都属于表现层的框架,它 是Spring框架的一部分,我们可以从Spring的整体结构中看得出来,如下图:

Spring MVC处理一次请求到响应的流程

快速入门

基本步骤

  1. 创建项目
  2. 导入jar包
  3. 创建springmvc.xml文件并配置Controller扫描
  4. 在web.xml文件中配置前端控制器
  5. 创建jsp文件用于测试
  6. 创建POJO
  7. 创建Controller类
  8. 进行测试
步骤详见附件
创建springmvc.xml文件并配置Controller扫描
SpringMVC本身就是Spring的子项目,对Spring兼容性很好,不需要做很多配置, 这里只配置一个Controller扫描,让Spring对页面控制层Controller进行管理
在web.xml文件中配置前端控制器
创建POJO
加入jsp页面
将jsp页面放置到WEB-INF/jsp文件夹下
创建Controller
根据jsp页面编写Controller类
  • Controller是一个普通的java类,不需要实现任何接口
  • 需要在类上添加@Controller注解,把Controller交由Spring管理
  • 在方法上面添加@RequestMapping注解,里面指定请求的url
    • 其中“.action”可以加也可以不加

Spring MVC的架构

框架结构

上图中,前端控制器(紫色)仅需要配置,处理器映射器、处理器适配器和视图解析器(灰色)是固有的组件,需要我们编码的仅有处理器和视图(橙色)

架构处理请求的流程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器根据请求url在Spring容器中(通过RequestMapping注解内容)寻找对应的处理器Controller(也叫后端控制器),若找到,为其生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  5. 执行处理器
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户
组件详细说明
以下组件通常使用框架提供实现:
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等
Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler
HandlerAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
下图是许多不同的适配器,最终都可以使用usb接口连接
ViewResolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户
View:视图
Spring MVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等,我们最常用的视图就是jsp
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
说明:在Spring MVC的各个组件中, 处理器映射器 处理器适配器 视图解析器称为 Spring MVC的三大组件。
需要用户开发的组件有 HandlerView

默认加载的组件

框架默认加载的组件配置文件位置如下
默认加载的组件已经过时,应该配置推荐使用的组件,见下文配置

组件扫描器

使用组件扫描器可以省去手动一一配置Controller类的繁复操作

注解式处理器映射器

注解式处理器映射器,会对类中标记了@ResquestMapping注解的方法进行映射。根据@ResquestMapping定义的url匹配@ResquestMapping标记的方法,匹配成功后返回HandlerMethod对象到前端控制器
HandlerMethod对象内部封装了url对应的方法Method
配置
从spring3.1版本开始,废除了DefaultAnnotationHandlerMapping的使用,推荐使用RequestMappingHandlerMapping完成注解式处理器映射
在springmvc.xml配置文件中配置如下

注解式处理器适配器

注解式处理器适配器,将对标记了@ResquestMapping注解的方法进行适配
配置
从spring3.1版本开始,废除了AnnotationMethodHandlerAdapter的使用,推荐使用RequestMappingHandlerAdapter完成注解式处理器适配
在springmvc.xml配置文件中配置如下

注解驱动

直接配置处理器映射器和处理器适配器比较麻烦,可以使用注解驱动来加载
SpringMVC使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置

视图解析器

可以理解为路径解析器
视图解析器使用SpringMVC框架默认的InternalResourceViewResolver, 这个视图解析器支持Jsp视图解析
在springmvc.xml配置文件中配置如下
逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为ItemList,则最终返回的jsp视图地址为:/ WEB-INF/jsp/itemList.jsp
最终jsp物理地址:前缀 +  逻辑视图名 + 后缀
Controller配合上述配置使用
此后编写路径时就不需要写全称了

Spring MVC整合MyBatis

目标:控制层使用Spring MVC,持久层使用MyBatis实现
详细见附件

基本步骤

  1. 导入依赖
  2. 加入配置文件
  3. 根据数据库表生成/编写POJO和映射文件并引入
  4. 测试
配置文件整合思路
Dao层
  • SqlMapConfig.xml,空文件即可,但是需要文件头
  • applicationContext-dao.xml
    • 数据库连接池
    • SqlSessionFactory对象,需要Spring和MyBatis整合包下的
    • 配置mapper文件扫描器
Service层
  • applicationContext-service.xml包扫描器,扫描@service注解的类
  • applicationContext-trans.xml配置事务
Controller层
  • springmvc.xml
    • 包扫描器,扫描@Controller注解的类
    • 配置注解驱动
    • 配置视图解析器
Web.xml文件
  • 配置Spring
  • 配置前端控制器
配置文件汇总
  • sqlMapConfig.xml
  • applicationContext-dao.xml
  • db.properties
  • applicationContext-service.xml
  • applicationContext-trans.xml
  • springmvc.xml
  • web.xml

参数绑定

若Controller需要接收参数,则涉及到Spring MVC的参数绑定
Struts2的Action类是单例的,可以直接用属性来接收请求参数,而Spring MVC的Controller不是单例的,就不可以用属性来接收请求参数了
Spring MVC的Controller绑定参数的方法是 在Controller类中对应方法添加一个形式参数,Spring MVC会将请求参数传递给对应方法

Spring MVC默认支持的参数类型

  • HttpServletRequest:通过request对象获取请求信息
  • HttpServlcetResponse:通过response处理响应信息
  • HttpSession:通过session对象得到session中存放的对象

通过这种方式也可以获取请求参数并封装响应信息传递到前台,但不建议使用

Model/ModelMap

Model
除了ModelAndView以外,还可以使用Model来向页面传递数据, Model是一个接口,在参数里直接声明model即可
如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代
不管是Model还是ModelAndView,其 本质都是使用Request对象向jsp传递数据
ModelMap
ModelMap是Model接口的实现类,也可以通过ModelMap向页面传递数据
使用Model和ModelMap的效果一样,如果直接使用Model,Spring MVC会实例化ModelMap
也可以使用返回String的方式来设定视图

绑定简单类型

当请求的参数名称和处理器形参 名称一致时会将请求参数与形参进行绑定
参数绑定支持的简单类型
  • 整形:Integer、int
  • 字符串:String
  • 单精度:Float、float
  • 双精度:Double、double
  • 布尔型:Boolean、boolean
    • 对于布尔类型的参数,请求的参数值为true/false或者1/0
    • 如:url为http://localhost:8080/xxx.action?id=2&status=false,处理器方法为public String editItem(Model model,Integer id,Boolean status) 
@RequestParam注解
@RequestParam注解常用于处理简单类型的绑定
其用法如下:
  • value:参数名字,即入参的请求参数名字
    • 如value=“itemId”表示请求的参数区中的名字为itemId的参数的值将传入
  • required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
  • defaultValue:默认值,表示如果请求中没有同名参数时的默认值
示例:

绑定POJO

如果请求参数较多,或提交的表单中内容很多时,可以使用简单类型接收数据,但更推荐使用POJO对象接收数据
使用方法:
POJO类中的属性应该与input内name属性一致
如图

解决修改post提交数据乱码问题

需要在web.xml文件中添加Spring的编码过滤器相关配置
另外,对于get请求中文参数出现乱码的解决方法有两个
绑定包装POJO
使用包装POJO接收参数
创建包装POJO类,修改前台页面,input的name属性应该加上被包装的POJO名称前缀,如下

自定义参数绑定

Spring MVC提供数据类型自定义转换功能,可以通过手动编写转换器转换规则来转换前台传入的数据类型,如:字符串按一定规则转换成日期
需求
在商品修改页面可以修改商品的生产日期,并且根据业务需求自定义日期格式。
需求分析
由于日期数据有很多种格式,Spring MVC没办法把字符串转换成日期类型,所以需要自定义参数绑定
前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定
可以在Spring MVC处理器适配器上自定义转换器Converter进行参数绑定
一般使用<mvc:annotation-driven/>注解驱动加载处理器适配器,可以在此标签上进行配置
在set标签内可以配置多个Converter
Converter代码如下

Spring MVC与Struts2的对比

  • Spring MVC的入口是一个servlet即前端控制器,而struts2入口是一个filter过滤器
  • Spring MVC基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),Struts2是基于类开发,传递参数是通过类的属性,只能设计为多例
  • Struts2采用值栈存储请求和响应的数据,通过OGNL存取数据Spring MVC通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面(Jsp视图解析器默认使用jstl)

Spring MVC进阶

高级参数绑定

绑定数组

可以在Controller的方法中添加String[]类型的形参,也可以使用POJO的String[]类型属性接收数组类型的请求参数
如下
绑定List
请求参数可以是存放多个POJO的List对象,通过在QueryVo中增加List<*>类型的属性可以接收List类型的参数
要求:jsp页面中input标签的name属性应该为List类型的属性名称 + 下标 + 元素属性
如下
实际前端HTML页面代码应该如下
注意
List类型不能直接由Controller中方法的形参接收,必须由POJO来间接接收List类型的参数
*关于foreach遍历标签
  • ${current}:当前这次迭代的(集合中的)项
  • ${status.first}判断当前项是否为集合中的第一项,返回值为true或false
  • ${status.last}判断当前项是否为集合中的最后一项
  • varStatus属性常用参数总结下:
    • ${status.index}输出行号,从0开始。
    • ${status.count}输出行号,从1开始。
    • ${status.next}后一项,返回值为true或false
    • begin、end、step分别表示:起始序号,结束序号,步长

@RequestMapping注解

通过@RequestMapping注解可以定义不同的处理器映射规则
又以下的功能

设置URL路径映射

用法:
  • 设置单个路径映射
    • @RequestMapping(value="/item")
      • 或当仅需要配置value属性时,可以省略value=:@RequestMapping("/item")
  • value值的类型是数组,可以将多个URL映射到同一个方法中
    • @RequestMapping(value = { "itemList", "itemListAll" })

设置通用请求前缀

在class类名前添加@RequestMapping(url)指定通用请求前缀,可以限制此类下的所有方法请求url必须以请求前缀开头
可以使用此方法来对URL进行分类管理
如下
此后访问这个Controller的特定方法的URL应该如:*/item/itemList.action

设置限定的请求方法

可以限定请求进入这个Controller的方法(post/get)
使用RequestMethod属性配置特定请求可入
其类型也是一个数组
  • 限定GET方法@RequestMapping(method = RequestMethod.GET)
    • 如果通过POST访问则报错:HTTP Status 405 - Request method 'POST' not supported
  • 限定POST方法:@RequestMapping(method = RequestMethod.POST)
    • 如果通过GET访问则报错:HTTP Status 405 - Request method 'GET' not supported
  • GET和POST均可:@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})
  • DELETE等等其他方法(国内少用)

Controller中方法的返回值

Controller中方法的返回值有以下几种
  • 返回ModelAndView
  • 返回void
  • 返回String

返回ModelAndView

Controller中方法中定义ModelAndView对象并返回,对象中可添加model数据,指定view视图
不推荐使用这种方式

返回void

在Controller方法形参上可以定义request和response,使用request或response指定响应结果来处理、控制请求,类似servlet开发
这种方式适合处理ajax的请求,因为处理ajax请求只需要返回(响应)数据,不需要返回视图,直接使用response对象返回json数据即可

返回String

携带数据
使用Model对象来携带数据传递
逻辑视图名
Controller方法返回字符串可以 指定逻辑视图名通过视图解析器解析为物理视图地址
Redirect重定向
Contrller方法返回字符串可以重定向到一个url地址
用法:在返回的字符串前加上 redirect: 字段
如:return "redirect:/itemEdit.action";
若带参数重定向:return "redirect:/itemEdit.action?itemId=" + item.getId();
Forward转发
某个Controller方法执行后继续执行另一个Controller方法
用法:类似重定向,加上 forward: 字段
如:return "forward: /itemEdit.action";
官方推荐使用返回String的方式开发Controller,这种方式可以将数据和视图分离、解耦,符合MVC架构的思想

异常处理器

Spring MVC在处理请求过程中出现异常信息将交由 异常处理器进行处理, 自定义异常处理器可以编写代码实现系统的异常处理逻辑

异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由Spring MVC前端控制器交由异常处理器进行异常处理,如下图

自定义异常类

为了区别不同的异常,通常根据异常类型进行区分,可以自定义一个异常类

自定义异常处理器

可以自定义一个异常处理器按照编写的逻辑处理接收到的异常
异常处理器要实现HandlerExceptionResolver接口,并重写resolveException方法
其中resolveException方法的参数意义如下
  • HttpServletRequest request:当前请求对象
  • HttpServletResponse response:当前响应对象
  • Object obj:发生异常的位置所在(异常发生在哪个类)
  • Exception e:当前发生的异常对象

配置异常处理器

在springmvc.xml文件配置

引入错误页面

Spring MVC异常处理基本开发思路

Spring MVC已经提供好了HandlerExceptionResolver接口供开发者实现,要处理异常:
  1. 首先编写自定义异常处理器,实现HandlerExceptionResolver接口
    1. 异常处理器内部编写异常处理逻辑
    2. 定义发生异常时转发到什么页面
  2. 配置异常处理器到Spring容器中
  3. 引入错误页面并配置发生异常时转发

上传图片

上传图片的功能其实就是接收图片/文件请求参数

上传图片的步骤

在Tomcat上配置虚拟目录
在Tomcat下conf/server.xml中添加<Context docBase="D:\develop\upload\temp" path="/pic" reloadable="false"/>
在IDEA配置Tomcat的虚拟目录如下图
访问对应地址(加上具体资源名称和后缀)即可访问本地目录下的图片

这步的目的是为了让前台页面通过上述设置的虚拟目录访问到这个图片( 毋需通过本地目录访问图片了
 
导入jar包依赖
  • commons-fileupload-1.2.2.jar
  • commons-io-2.4.jar
配置文件上传解析器
在springmvc.xml中配置文件上传解析器
文件上传解析器配置的bean的id必须为multipartResolver
修改jsp页面
在jsp页面中form标签添加enctype属性,值为multipart/form-data
在Controller对应方法中添加图片上传逻辑
按照这个步骤开发的文件上传功能可以将上传的文件(目录)与发布的war包分离,互相独立不影响

JSON数据交互

@RequestBody注解

JSON  -->  Java对象
@RequestBody注解用于读取HTTP 请求的内容(字符串),然后可以通过Spring MVC提供的 HttpMessageConverter接口将读到的内容(json数据)转换为Java对象并绑定到Controller方法的参数上

@ResponseBody

Java对象 --> JSON
@ResponseBody注解用于Controller中的方法返回的对象,可以通过Spring MVC提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

实现JSON数据交互的步骤

引入依赖的jar包
Gson/Jackson/FastJson/等等
此处使用Jackson
编写Controller
安装浏览器的测试工具
如Postman等
测试
*若不使用注解驱动(annotation-driven)则需要手动配置JSON转换器
如果不使用注解驱动<mvc:annotation-driven/>,就需要给处理器适配器配置json转换器, 参考之前学习的自定义参数绑定

RESTful支持

RESTful简介

RESTful就是一个 资源定位资源操作风格
RESTful不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用:POST、DELETE、PUT、GET,使用不同方法对资源进行操作, 分别对应:添加、删除、修改、查询
通过请求method的不同来区分这次请求要怎样操作资源

Spring MVC支持的RESTful

通过@RequestMapping注解可以识别RESTful风格的请求
  • 其中{*}为占位符
  • 使用@PathVariable注解可以获取URL上的数据
  • 如果@RequestMapping中表示为"item/{id}",id和形参名称一致,@PathVariable不用指定名称,如果不一致,例如"item/{ItemId}"则需要指定名称@PathVariable("itemId")
  • @PathVariable是获取url上数据的,而@RequestParam获取请求参数的(包括post表单提交)
  • 如果加上@ResponseBody注解,就不会走视图解析器,不会返回页面,目前返回的json数据,如果不加,就走视图解析器,返回页面

拦截器

Spring Web MVC的处理器拦截器 类似于Servlet开发中的过滤器Filter过滤器,用于对处理器进行 预处理后处理

拦截器定义

拦截器内可以定义三个方法,分别对应三个时间点
  • preHandle:执行Controller方法前
  • postHandle:执行Controller方法后
  • afterCompletion:页面渲染后
拦截器需要实现HandlerInterceptor接口

拦截器配置

在spirngmvc.xml中配置拦截器

测试

多个拦截器的拦截规则

详见附件
配置两个拦截器HandlerInterceptor1和HandlerInterceptor2
正常运行流程
中断运行流程
HandlerInterceptor1的preHandler方法返回false,HandlerInterceptor2返回true,运行流程如下:
HandlerInterceptor1..preHandle..
从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且Controller也不执行了
HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false,运行流程如下:
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor1..afterCompletion..
从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且 Controller也不执行了
结论
  • preHandle按拦截器配置顺序调用
  • postHandle按拦截器配置逆序调用
  • afterCompletion按拦截器定义逆序调用
  • postHandler在拦截器链内所有拦截器返true成功才调用
  • afterCompletion只有同一个拦截器定义的preHandle返回true才调用
preHandle和postHandle类似俄罗斯套娃

拦截器应用示例

需求
  1. 有一个登录页面,需要写一个Controller访问登录页面
  2. 登录页面有一提交表单的动作。需要在Controller中处理。
    1. 判断用户名密码是否正确(在控制台打印)
    2. 如果正确,向session中写入用户信息(写入用户名username)
    3. 跳转到商品列表
  3. 拦截器。
    1. 拦截用户请求,判断用户是否登录(登录请求不能拦截)
    2. 如果用户已经登录则放行
    3. 如果用户未登录,跳转到登录页面
详细开发细节见附件
细节
只拦截访问商品的URL请求,修改ItemController,让所有的请求都必须以item开头
配置拦截器时使用以下方式拦截对/item/开头的URL的请求

附件列表

    猜你喜欢

    转载自www.cnblogs.com/zella1996/p/9550449.html