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)。
可以代替上边的处理器映射器和适配器的配置。
在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。
注解开发:
数据回显:表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。
集合类型(String[]、List<>、map(自学))的参数绑定。
springmvc上传图片(重点)
json数据交互(提交json数据、响应json数据)(重点)
Validation(springmvc使用校验方式,使用Hibernate Validator(和Hibernate的ORM没有任何关系))
异常处理器(可以用于系统的统一异常处理,架构的内容)
springmvc提供RESTful支持
拦截器(用于权限控制)
表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。
对商品修改数据回显:
注意在进入修改页面的controller方法中和提交修改商品信息方法model.addAttribute方法设置的key一致。
修改商品显示方法:
修改商品页面:
修改商品提交方法:
使用Model.addtribute方法进行数据回显:
使用@ModelAttribute,作用于将请求pojo数据放到Model中回显到页面
在ModelAttribute方法指定的名称就是要填充Model中的key,在页面中就要通过key取数据。
需求:商品类别信息在商品信息页面显示。
页面:
使用@ModelAttribute将公用的取数据的方法返回值传到页面,不用在每一个controller方法通过Model将数据传到页面。
需求:在商品查询列表页面,用户选择要删除的商品,批量删除商品。
在controller方法中如何将批量提交的数据绑定成数组类型。
需求:批量修改商品信息提交。
先进入批量修改商品页面,填写信息,点击提交。
注释:
itemsList:controller方法形参包装类型中list的属性名。
itemsList[0]或itemsList[1]。。,[]中是序号,从0开始。
itemsList[].name:name就是controller方法形参包装类型中list中pojo的属性名
使用包装类型接收页面批量提交的数据,绑定成List。
springmvc是通过方法的形参接收参数,在使用时可以以单例方式使用,建议使用单例。
struts是通过成员变量接收参数,在使用时必须以多例方式使用。
springmvc是基于方法开发,
struts基于类开发。
springmvc将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
springmvc开发以方法为单位进行开发,方法更帖进service(业务方法)。
经过实际测试,发现struts标签解析速度比较慢(Struts标签),建议在实际开发时使用jstl。
在商品修改页面,增加图片上传的功能。
操作流程:
用户进入商品修改页面
上传图片
点击提交(提交的是图片和商品信息)
再次进入修改页面,图片在商品修改页面展示
切记:不要把图片上传到工程目录 ,不方便进行工程维护。
实际电商项目中使用专门图片服务器(http,比如apache、tomcat)。
本教程使用图片虚拟目录,通过虚拟目录 访问硬盘上存储的图片目录 。
虚拟目录设置:
注意:
图片目录中尽量进行目录分级存储,提高访问速度(提交i/o)。
springmvc使用commons-fileupload进行图片上传。
commons-fileupload对应的springmvc的图片上传解析器:
org.springframework.web.multipart.commons.CommonsMultipartResolver
加入commons-fileupload的jar包
@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。
@ResponseBody该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端
json数据格式是比较简单容易理解,json数据格式常用于远程接口传输,http传输json数据,非常方便页面进行提交/请求结果解析,对json数据的解析。
优点:客户端解析方便,在安卓手机使用较多(数据传来传去)
Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包,如下:
让处理器适配器支持json数据解析,需要注入MappingJacksonHttpMessageConverter。
注意:如果使用 则不用定义上边的内容。
@RequestBody:将请求的json数据转成java对象
@ResponseBody:将java对象转成json数据输出。
controller方法:
页面:
测试跟踪:
controller方法:
页面:
测试:
如果前端处理没有特殊要求建议使用第二种,请求key/value,响应json,方便客户端解析请求结果。
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
配置方式1:
配置方式2:
在classpath下创建CustomValidationMessages.properties
如果在eclipse中编辑properties文件无法看到中文则参考“Eclipse开发环境配置-indigo.docx”添加propedit插件。
需求:
商品信息提交时校验 ,商品生产日期不能为空,商品名称长度在1到30字符之间
需要修改controller方法,在要校验的pojo前边加上@Validated,
注意:添加@Validated表示在对items参数绑定时进行校验,校验信息写入BindingResult中,在要校验的pojo后边添加BingdingResult, 一个BindingResult对应一个pojo,且BingdingResult放在pojo的后边。
错误信息输出:
页头:
<%@ 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" %>
在需要显示错误信息地方:
${error.defaultMessage }
说明:
表示如果item参数绑定校验错误下边显示错误信息。
如果两处校验使用同一个Items类则可以设定校验分组。
定义分组:
分组就是一个标识,这里定义一个接口:
需求:
针对不同的controller方法通过分组校验达到个性化校验的目的,修改商品修改功能,只校验生产日期不能为空。
第一步:创建分组接口
第二步:定义校验规则属于哪个分组
第三步:在controller方法定义使用校验的分组
在@Validated中添加value={ValidGroup1.class}表示商品修改使用了ValidGroup1分组校验规则,也可以指定多个分组中间用逗号分隔,
@Validated(value={ValidGroup1.class,ValidGroup2.class })
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
一般项目中都需要作异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。
系统中异常类型有哪些?
包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常。
系统中异常包括两类:预期异常和运行时异常RuntimeException,
前者通过捕获异常从而获取异常信息,
后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类:
CustomException,用于测试。
要在一个统一异常处理的类中要处理系统抛出的所有异常,根据异常类型来处理。
统一异常处理的类是什么?
前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常,进行异常处理。
在系统中自定义统一的异常处理器,写系统自己的异常处理代码。
统一异常处理器实现HandlerExceptionResolver接口。
在springmvc.xml中添加:
或者:
根据不同的异常类型进行异常处理。
系统自定义的异常类是CustomException ,在controller方法中、service方法中手动抛出此类异常。
在统一异常处理器CustomExceptionResolver中实现上边的逻辑。
可以在controller方法、service方法、dao实现类中抛出异常,要求dao、service、controller遇到异常全部向上抛出异常,方法向 上抛出异常throws Exception
图解:
RESTful软件开发理念(或规范),RESTful对http进行非常好的诠释。
RESTful即Representational State Transfer的缩写。
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。
REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,
总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....
RESTful的url是简洁的:http:// localhost:8080/items/editItems/1
参数通过url传递,rest接口返回json数据
根据id查看商品信息,商品信息查看的连接使用RESTful方式实现,商品信息以json返回。
@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 itemsList = itemService.findItemsList(null);
// 创建modelAndView准备填充数据、设置视图
ModelAndView modelAndView = new ModelAndView();
// 填充数据
modelAndView.addObject("itemsList", itemsList);
// 视图
modelAndView.setViewName("item/itemsList");
return modelAndView;
}
当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就报错:
需要通过设置对静态资源进行解析.
spring mvc 的实现对静态资源进行映射访问。
如下是对js文件访问配置:
访问/js/**的url从工程下/js/下解析。
Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
用户请求到DispatherServlet中,DispatherServlet调用HandlerMapping查找Handler,
HandlerMapping返回一个拦截的链儿(多个拦截),
springmvc中的拦截器是通过HandlerMapping发起的。
在企业开发,使用拦截器实现用户认证(用户登陆后进行身份校验拦截),用户权限拦截。
配置全局的拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping。
在springmvc.xml中配置:
定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2,每个拦截器的preHandler方法都返回true。
测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
执行preHandle是顺序执行。
执行postHandle、afterCompletion是倒序执行
测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且controller也不执行了。
如果preHandle不放行,postHandle、afterCompletion都不执行。
只要有一个拦截器不放行,controller不能执行完成
测试结果:
HandlerInterceptor1...preHandle
总结:
从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且controller也不执行了
只有前边的拦截器preHandle方法放行,下边的拦截器的preHandle才执行。
总结:
preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用
postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用
将日志拦截器或异常拦截器放在拦截器链儿中第一个位置,且preHandle方法放行
用户访问系统的资源(url),如果用户没有进行身份认证,进行拦截,系统跳转登陆页面,如果用户已经认证通过,用户可以继续访问系统 的资源。
拦截实现思路:
在springmvc.xml中配置拦截器: