springmvc 2

springmvc 注解开发高级知识

1 复习

2018/5/8

springmvc框架:

用户请求url到DispatcherServlet前端控制器,相当于中央调度器,降低系统各组件之间耦合度

DispatcherServlet前端控制器通过HandlerMapping根据url找到Handler。

DispatcherServlet前端控制器通过HandlerAdapter处理器适配器执行Handler。

DispatcherServlet前端控制器拿着Handler返回的ModelAndView通过视图解析器ViewResolver去进行视图解析。

视图解析:将程序中写的逻辑视图名,转成真正的视图(springmvc通过view表示各各不同类型的视图)。

DispatcherServlet前端控制器调用View的渲染方法进行视图渲染(将ModelAndView中的Model放到request域)。

要掌握springmvc的注解开发,企业中常用springmvc注解开发。

使用专门注解处理器映射器(RequestMappingHandlerMapping)和处理器适配器(RequestMappingHandlerAdapter)。

<mvc:annotation-driven/>可以代替上边的处理器映射器和适配器的配置。

在Handler(Controller)中定义很多的方法,一个方法通过RequestMapping和url进行映射。

方法返回值:ModelAndView、string(jsp的逻辑视图名)、void(通过response将数据输出成json)

方法输入参数(形参):springmvc需要将请求的key/value(串,id=001&type=t002)、解析、绑定到Handler(Controller)中方法的形参上。

springmvc默认支持多类型的参数绑定。

默认支持哪些类型:
HttpServletRequest、response、session、Model(用于将数据填充到request域)

@requestParam注解:用于绑定单个请求参数,常用于简单类型参数(Integer、String 、Float。。。)绑定。
不用 @requestParam,要求:请求参数的名称和方法形参名一致方可绑定。

对于简单类型参数中的日期型,建议使用自定义参数绑定,对日期型数据个化定义日期的格式。

自定义参数绑定:建议使用Convertor进行参数绑定。

还可以绑定pojo、包装的pojo。

2 目录

注解开发:

数据回显:表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。
集合类型(String[]、List<>、map(自学))的参数绑定。
springmvc上传图片(重点)
json数据交互(提交json数据、响应json数据)(重点)
Validation(springmvc使用校验方式,使用Hibernate Validator(和Hibernate的ORM没有任何关系))
异常处理器(可以用于系统的统一异常处理,架构的内容)
springmvc提供RESTful支持

拦截器(用于权限控制)

3 数据回显

3.1 需求

表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。

3.2 对简单类型的数据回显

对商品修改数据回显:
注意在进入修改页面的controller方法中和提交修改商品信息方法model.addAttribute方法设置的key一致。
修改商品显示方法:

修改商品页面:

修改商品提交方法:

3.3 pojo类型数据回显

3.3.1 方法1:

使用Model.addtribute方法进行数据回显:

3.3.2 方法2:

使用@ModelAttribute,作用于将请求pojo数据放到Model中回显到页面

在ModelAttribute方法指定的名称就是要填充Model中的key,在页面中就要通过key取数据。

3.3.3 @ModelAttribute将方法返回值传到页面

需求:商品类别信息在商品信息页面显示。

页面:

使用@ModelAttribute将公用的取数据的方法返回值传到页面,不用在每一个controller方法通过Model将数据传到页面。

4 参数绑定集合类型

4.1 绑定数组

需求:在商品查询列表页面,用户选择要删除的商品,批量删除商品。
在controller方法中如何将批量提交的数据绑定成数组类型。

4.1.1 页面定义

4.1.2 controller方法定义

4.2 绑定List

需求:批量修改商品信息提交。
先进入批量修改商品页面,填写信息,点击提交。

4.2.1 页面定义


注释:

itemsList:controller方法形参包装类型中list的属性名。
itemsList[0]或itemsList[1]。。,[]中是序号,从0开始。
itemsList[].name:name就是controller方法形参包装类型中list中pojo的属性名

4.2.2 controller方法定义

使用包装类型接收页面批量提交的数据,绑定成List。

5 springmvc和struts的区别

springmvc是通过方法的形参接收参数,在使用时可以以单例方式使用,建议使用单例。
    struts是通过成员变量接收参数,在使用时必须以多例方式使用。
springmvc是基于方法开发,
    struts基于类开发。
springmvc将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
springmvc开发以方法为单位进行开发,方法更帖进service(业务方法)。
    经过实际测试,发现struts标签解析速度比较慢(Struts标签),建议在实际开发时使用jstl。

6 商品图片上传

6.1 需求

在商品修改页面,增加图片上传的功能。
操作流程:

用户进入商品修改页面
上传图片
点击提交(提交的是图片和商品信息)
再次进入修改页面,图片在商品修改页面展示

6.2 图片存储问题

切记:不要把图片上传到工程目录 ,不方便进行工程维护。
实际电商项目中使用专门图片服务器(http,比如apache、tomcat)。
本教程使用图片虚拟目录,通过虚拟目录 访问硬盘上存储的图片目录 。
虚拟目录设置:

注意:
图片目录中尽量进行目录分级存储,提高访问速度(提交i/o)。

6.3 配置图片上传解析器

springmvc使用commons-fileupload进行图片上传。
commons-fileupload对应的springmvc的图片上传解析器:

org.springframework.web.multipart.commons.CommonsMultipartResolver


加入commons-fileupload的jar包

6.4 编写上传图片的页面

6.5 编写controller方法

7 json数据的交互

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

@ResponseBody该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

7.1 需求

json数据格式是比较简单容易理解,json数据格式常用于远程接口传输,http传输json数据,非常方便页面进行提交/请求结果解析,对json数据的解析。
优点:客户端解析方便,在安卓手机使用较多(数据传来传去)

7.2 springmvc解析json加入json解析包

Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包,如下:

7.3 在处理器适配器中注入MappingJacksonHttpMessageConverter

让处理器适配器支持json数据解析,需要注入MappingJacksonHttpMessageConverter。

注意:如果使用<mvc:annotation-driven /> 则不用定义上边的内容。

7.4 @RequestBody和@ResponseBody

@RequestBody:将请求的json数据转成java对象
@ResponseBody:将java对象转成json数据输出。

7.5 请求json响应json

controller方法:

页面:

测试跟踪:

7.6 请求key/value响应json

controller方法:

页面:

测试:

7.7 小结

如果前端处理没有特殊要求建议使用第二种,请求key/value,响应json,方便客户端解析请求结果。

8 validation校验(了解)

b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的,本节主要学习springmvc实现控制层添加校验。

Spring3支持JSR-303验证框架,JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,官方参考实现是Hibernate Validator(与Hibernate ORM 没有关系)
JSR 303 用于对Java Bean 中的字段的值进行验证。

对前端的校验大多数通过js在页面校验,这种方法比较简单,如果对安全性考虑,还要在后台校验。
springmvc使用JSR-303(javaEE6规范的一部分)校验规范,springmvc使用的是Hibernate Validator

8.1 加入Hibernate Validator的jar

8.2 在处理器适配器中配置校验器

配置方式1:

<!-- 自定义webBinder -->
<bean id="customBinder"
    class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
    <property name="validator" ref="validator" />
</bean>

<!-- 注解适配器 -->
<bean
    class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer" ref="customBinder"></property>
</bean>

配置方式2:

<mvc:annotation-driven validator="validator"> </mvc:annotation-driven>

8.3 创建CustomValidationMessages

在classpath下创建CustomValidationMessages.properties

如果在eclipse中编辑properties文件无法看到中文则参考“Eclipse开发环境配置-indigo.docx”添加propedit插件。

8.4 校验规则

需求:
商品信息提交时校验 ,商品生产日期不能为空,商品名称长度在1到30字符之间

8.5 捕获错误

需要修改controller方法,在要校验的pojo前边加上@Validated,

注意:添加@Validated表示在对items参数绑定时进行校验,校验信息写入BindingResult中,在要校验的pojo后边添加BingdingResult, 一个BindingResult对应一个pojo,且BingdingResult放在pojo的后边。

错误信息输出:

8.6 在页面上展示错误


页头:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

在需要显示错误信息地方:

<spring:hasBindErrors name="item">
<c:forEach items="${errors.allErrors}" var="error">
   ${error.defaultMessage }<br/>
</c:forEach>
</spring:hasBindErrors>

说明:

<spring:hasBindErrors name="item">表示如果item参数绑定校验错误下边显示错误信息。

8.7 分组校验

如果两处校验使用同一个Items类则可以设定校验分组。
定义分组:

分组就是一个标识,这里定义一个接口:

需求:
针对不同的controller方法通过分组校验达到个性化校验的目的,修改商品修改功能,只校验生产日期不能为空。
第一步:创建分组接口

第二步:定义校验规则属于哪个分组

第三步:在controller方法定义使用校验的分组

在@Validated中添加value={ValidGroup1.class}表示商品修改使用了ValidGroup1分组校验规则,也可以指定多个分组中间用逗号分隔,

@Validated(value={ValidGroup1.class,ValidGroup2.class })

9 统一异常处理

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。

9.1 需求

一般项目中都需要作异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。

系统中异常类型有哪些?

包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常。

9.1.1 异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,

前者通过捕获异常从而获取异常信息,
后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

9.2 统一异常处理解决方案

9.2.1 定义异常

针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类:
CustomException,用于测试。

9.2.2 异常处理

要在一个统一异常处理的类中要处理系统抛出的所有异常,根据异常类型来处理。
统一异常处理的类是什么?
前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常,进行异常处理。

在系统中自定义统一的异常处理器,写系统自己的异常处理代码。

9.2.2.1 定义统一异常处理器类

统一异常处理器实现HandlerExceptionResolver接口。

9.2.2.2 配置统一异常处理器

在springmvc.xml中添加:

<!-- 异常处理器 -->
<bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>

或者:

9.2.2.3 异常处理逻辑

根据不同的异常类型进行异常处理。
系统自定义的异常类是CustomException ,在controller方法中、service方法中手动抛出此类异常。

  1. 针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示。
  2. 针对非CustomException异常,对这类重新构造成一个CustomException,异常信息为“未知错误”,此类错误需要在系统测试阶段去排除。

在统一异常处理器CustomExceptionResolver中实现上边的逻辑。

9.2.2.4 测试抛出异常由统一异常处理器捕获

可以在controller方法、service方法、dao实现类中抛出异常,要求dao、service、controller遇到异常全部向上抛出异常,方法向 上抛出异常throws Exception

图解:

10 RESTful支持

10.1 什么是RESTful

RESTful软件开发理念(或规范),RESTful对http进行非常好的诠释。
RESTful即Representational State Transfer的缩写。

要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,
总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。

URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

综述

综合上面的解释,我们总结一下什么是RESTful架构:
  (1)每一个URI代表一种资源;
  (2)客户端和服务器之间,传递这种资源的某种表现层
  (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

10.2 url的RESTful实现

非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....
RESTful的url是简洁的:http:// localhost:8080/items/editItems/1
参数通过url传递,rest接口返回json数据

10.2.1 需求

根据id查看商品信息,商品信息查看的连接使用RESTful方式实现,商品信息以json返回。

10.2.2 第一步更改DispatcherServlet配置

10.2.3 第二步参数通过url传递

@RequestMapping(value="/ editItem/{item_id}"):
    {×××}占位符,请求的URL可以是“/editItem/1”或“/editItem/2”,
    通过在方法中使用@PathVariable获取{×××}中的×××变量。

如果RequestMapping中表示为"/ editItem/{id}",id和形参名称一致,@PathVariable不用指定名称。

商品查询的controller方法也改为rest实现:

// 查询商品列表
@RequestMapping("/queryItem")
public ModelAndView queryItem() throws Exception {
    // 商品列表
    List<Items> itemsList = itemService.findItemsList(null);

    // 创建modelAndView准备填充数据、设置视图
    ModelAndView modelAndView = new ModelAndView();

    // 填充数据
    modelAndView.addObject("itemsList", itemsList);
    // 视图
    modelAndView.setViewName("item/itemsList");

    return modelAndView;
}

10.2.4 设置静态资源解析

当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就报错:

需要通过设置对静态资源进行解析.

spring mvc 的<mvc:resources mapping="" location="">实现对静态资源进行映射访问。

如下是对js文件访问配置:

访问/js/**的url从工程下/js/下解析。

11 springmvc拦截器

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

11.1 拦截器的异常场合

用户请求到DispatherServlet中,DispatherServlet调用HandlerMapping查找Handler,
    HandlerMapping返回一个拦截的链儿(多个拦截),
        springmvc中的拦截器是通过HandlerMapping发起的。

在企业开发,使用拦截器实现用户认证(用户登陆后进行身份校验拦截),用户权限拦截。

11.2 springmvc拦截器方法

11.3 测试拦截器

11.3.1 定义两个拦截器

11.3.2 配置拦截器

针对某种mapping配置拦截器
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
    <list>
        <ref bean="handlerInterceptor1"/>
        <ref bean="handlerInterceptor2"/>
    </list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
针对所有mapping配置全局拦截器

配置全局的拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping。
在springmvc.xml中配置:

11.3.3 测试1 (1 号和2号都放行)

定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2,每个拦截器的preHandler方法都返回true。

测试结果:

HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle

HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle

HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion

总结:
执行preHandle是顺序执行。
执行postHandle、afterCompletion是倒序执行

11.3.1 测试2 (1 号放行和2号不放行)

测试结果:

HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion

总结:
从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且controller也不执行了。

如果preHandle不放行,postHandle、afterCompletion都不执行。
只要有一个拦截器不放行,controller不能执行完成

11.3.1 测试3 (1 号不放行和2号不放行)

测试结果:

HandlerInterceptor1...preHandle

总结:
从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且controller也不执行了

只有前边的拦截器preHandle方法放行,下边的拦截器的preHandle才执行。
总结:

preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用

11.3.2 日志拦截器或异常拦截器要求

将日志拦截器或异常拦截器放在拦截器链儿中第一个位置,且preHandle方法放行

11.4 拦截器应用(用户认证拦截)

11.4.1 需求

用户访问系统的资源(url),如果用户没有进行身份认证,进行拦截,系统跳转登陆页面,如果用户已经认证通过,用户可以继续访问系统 的资源。

11.4.2 用户登陆及退出功能开发

11.4.3 用户身份认证校验拦截器

拦截实现思路:

11.4.4 拦截器


在springmvc.xml中配置拦截器:


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值