JAVAWEB开发之SpringMVC详解(二)——高级开发、数据回显、参数绑定集合、图片上传、json交互、validation校验、异常处理、RESTful支持、拦截器

来源:互联网 发布:python数据分析怎么样 编辑:程序博客网 时间:2024/05/01 09:03

知识回顾

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&id=002)、解析绑定到Handler(controller)中方法的形参上。
springmvc默认支持多类型的参数绑定。
默认支持的类型:HttpServletRequest、HttpServletResponse、HttpSession、Model(用于将数据填充到request域)。
@RequestParam注解:用于绑定单个请求参数,常用于简单类型参数(Integer、String、Float......)绑定。
不使用@ReuestParam要求请求参数的名称和方法形参名一致方可进行绑定。
对于简单类型参数中的日期型,建议使用自定义参数绑定,对日期类型数据个性化定义日期的格式。
自定义参数绑定:建议使用Converter进行参数绑定。
还可以绑定pojo、包装的pojo.

高级知识清单

注解开发:
  数据回显:表单提交错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。
  集合类型(String[]、List<>、Map)的参数绑定
  springmvc上传图片(重点)
  json数据交互(提交json数据、响应json数据)(重点)
  Validation(springmvc使用校验方式: 使用Hibernate Validator(和Hibernate的ORM没有任何关系) )
拦截器(用于权限控制)

数据回显

需求  

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

对简单类型的数据回显

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

修改商品显示方法:
 @RequestMapping(value="/editItems",method={RequestMethod.GET})   public String editItems(Model model,Integer id) throws Exception{   // 将id传递到页面  model.addAttribute("id",id);    // 调用Service查询商品信息   ItemsCustom itemsCustom = itemsService.findItemsById(id); //将模型数据传到jsp   model.addAttribute("item",itemsCustom);    //return "editItem_2";  return "editItem";   }
修改页面
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" ><input type="hidden" name="id" value="${id }"/>修改商品信息:<table width="100%" border=1><tr><td>商品名称</td><td><input type="text" name="name" value="${item.name }"/></td></tr> ......
修改商品提交方法
@RequestMapping("/editItemSubmit")public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 进行数据回显model.addAttribute("id", id);// 提交后回到修改页面return "editItem";}
提交后查看网页源码

可以发现只有id回显到了修改页面
其过程原理是这样的:
第一次点击修改跳转到修改页面,从request域取值为name的id的属性赋值。
点击提交后将name的值(请求参数)提交到提交方法中为形参id赋值。
然后在提交方法内将id存入request域,再跳转到编辑页面,进行回显。

pojo数据类型回显

回显方法1:

使用model.addAttribute方法进行数据回显
@RequestMapping("/editItemSubmit")public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 进行数据回显model.addAttribute("id", id);model.addAttribute("item",itemsCustom);// 提交后回到修改页面return "editItem";}
过程就是:提交后表单数据传入到提交方法的形参id和itemsCustom中。
然后将形参存入到request域,属性命名保持与编辑页面取数据的名称一致。
跳转到编辑页面即可从request取值 进行回显。

回显方法2:

使用@ModelAttribute, 作用于将请求pojo数据放到Model中回显到页面
public String editItemSubmit(Model model,Integer id, @ModelAttribute(value="item")ItemsCustom itemsCustom) throws Exception {
在@ModelAttribute方法指定的名称就是要填充到Model中的key,在页面上就要通过key取数据。

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

需求:商品类别信息在商品信息页面展示
// 单独将商品类型的方法提出来,将方法返回值填充到request,在页面显示@ModelAttribute("itemsType")public Map<String, String> getItemsType() throws Exception{HashMap<String, String> itemsType = new HashMap<String, String>();itemsType.put("001", "数码");itemsType.put("002", "服装");return itemsType;}
页面
商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select>
使用@ModelAttribute将公用的取数据的方法返回值传递到页面,不用在controller的每一个方法中通过Model将数据传到页面。

参数绑定集合类型

绑定数组

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

页面定义

  <td><input type="checkbox" name="delete_id" value="${item.id }"/></td>

controller方法定义

// 删除商品@RequestMapping("/deleteItems")public String deleteItems(Integer[] delete_id) throws Exception{System.out.println(delete_id);return "success";}
运行结果:

绑定List<Object>

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

页面定义

<c:forEach items="${itemsList }" var="item" varStatus="s"><tr><td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td><td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"/></td>......</tr>
注释:name值中的itemList
itemsList:controller方法形参包装类型中list的属性名
itemsList[0]或itemsList[0]......[]中是序号,从0开始
itemsList[index].name就是controller方法形参包装类型中list中pojo的属性名。

Controller方法定义

使用包装类型接收页面批量提交的数据,绑定成list
public class ItemsQueryVo {// 商品信息private ItemsCustom itemsCustom;// 定义一个Listprivate List<ItemsCustom> itemsList;        ......}
使用包装类型作为形参 接收参数
// 批量修改商品提交@RequestMapping("/editItemsListSubmit")    public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){return "success";}

springmvc和struts的区别

SpringMVC是通过方法的形参接受参数,在使用时可以以单例方式使用,建议使用单例。
Struts是通过成员变量接收参数,在使用时必须以多例方式使用。

SpringMVC是基于方法开发,Struts是基于类开发。
SpringMVC将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
SpringMVC开发是以方法为单位进行开发,方法更贴近Service(业务方法)。
经过实际测试,发现Struts标签解析速度比较慢,建议在实际开发中使用jstl。

商品图片上传

需求

在商品修改页面,增加图片上传的功能。
操作流程:
    用户进入商品修改页面
    上传图片
    点击提交(提交的是图片和商品的信息)
    再次进入修改页面,图片在商品修改页面展示

图片存储的问题

切记:不要将图片上传到工程目录,不方便进行工程维护。
实际电商项目中使用专门的图片服务器(比如Apache、Tomcat)
在Tomcat中进行虚拟目录的设置
设置方法如下:
在Tomcat下,找到conf文件下的server.xml,打开,在<Host>和</host>之间加上如下代码,然后就部署完成,重启服务器,浏  览器可以访问:
  <Context path="/虚拟目录名" docBase="目标目录位置" debug="0" reloadable="true" ></Context>
 虚拟目录名:浏览器访问的地址
 目标目录位置:项目所在目录的绝对路径

 reloadable="true" :服务器配置动态加载

测试:拖进目录中一张图片,启动服务器进行测试:


注意:图片目录中尽量进行目录分级存储,提高IO的访问速度。

配置图片上传解析器

springmvc使用commons-fileupload进行图片上传
commons-fileupload对应的springmvc的图片上传解析器:org.springframework.web.multipart.commons.CommonsMultipartResolver
在springmvc.xml中配置如下:
<!-- 文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置上传文件的最大尺寸为5MB --><property name="maxUploadSize"><value>5242880</value></property></bean>
加入commons-fileupload的jar包

编写上传图片的页面

表单属性的enctype要设置为multipart/form-data
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data">
添加图片上传组件<input type="file" name=""> 其中name的属性值要与控制器中接收图片的方法中的形参一致。
<td>商品图片</td><td><c:if test="${item.pic != null }"><img src="/pic/${item.pic}" width="100px" height="100px"/><br/></c:if><input type="file" name="pictureFile"/></td>

编写控制器方法

@RequestMapping("/editItemSubmit")// public String editItemSubmit(Integer id,ItemsCustom// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{public String editItemSubmit(Model model,Integer id, @ModelAttribute(value="item")ItemsCustom itemsCustom,// 上传图片MultipartFile pictureFile) throws Exception {// 进行数据回显model.addAttribute("id", id);//model.addAttribute("item",itemsCustom);//进行图片上传if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {// 图片上传成功后,将图片的地址写到数据库String filePath = "/Users/liuxun/Desktop/pictures";// 上传文件原始名称String originFileName = pictureFile.getOriginalFilename();// 新的图片的名称String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));// 新文件File file = new File(filePath+File.separator+newFileName);// 将内存中的文件写入磁盘pictureFile.transferTo(file);// 图片上传成功,将图片地址写入数据库itemsCustom.setPic(newFileName);}// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 提交后回到修改页面return "editItem";// 请求重定向//return "redirect:queryItems.action";// 转发// return "forward:queryItems.action";}
提交后页面效果如下


json数据交互

需求

json数据格式是比较简单和容易理解的,json数据格式常用于远程接口传输,http传输json数据,非常方便页面进行 提交/请求结果解析,对json数据的解析。

SpringMVC解析json加入json解析包

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

在处理器适配器中注入MappingJacksonHttpMessageConverter

让处理器适配器支持json数据解析,需要注入MappingJacksonHttpMessageConverter
 <!-- 注解适配器 -->    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">    <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 -->    <property name="webBindingInitializer" ref="customBinder"/>    <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 -->    <property name="messageConverters">    <list>    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>    </list>    </property>    </bean>
需要将支持json数据解析的消息转换器注入到注解适配器中。

@RequestBody和@RespoonseBody

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

请求json响应json

controller方法
//请求的json响应json,请求商品信息,商品信息使用json格式,输出商品信息@RequestMapping("/requestJson")public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{return itemsCustom;}
页面(需要引入jQuery)
// 请求json响应jsonfunction requestJson(){$.ajax({url:"${pageContext.request.contextPath}/requestJson.action",type:"post",contentType:"application/json;charset=utf-8",//请求json数据 使用json表示商品信息data:'{"name":"手机","price":1999}',success:function(data){alert(data.name);}});}
测试:

请求key/value响应json

controller方法
@RequestMapping("/responseJson")public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{return itemsCustom;}
页面
// 请求key/value响应jsonfunction responseJson(){$.ajax({url:"${pageContext.request.contextPath}/responseJson.action",type:"post",//contentType:"application/json;charset=utf-8",//请求key/value数据 使用地址拼接表示商品信息data:"name=手机&price=1999",success:function(data){alert(data.name);}});}
测试:

总结:

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

validation校验

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

加入Hibernate Validator的jar


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

在注解适配器中注入自定义的webBinder
  <!-- 注解适配器 -->    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">    <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 -->    <property name="webBindingInitializer" ref="customBinder"/>    <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 -->    <property name="messageConverters">    <list>    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>    </list>    </property>    </bean>
在webBinder中注入自定义的校验器
<!-- 自定义webBinder --><bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"><!-- 使用converter进行参数转换 --><property name="conversionService" ref="conversionService"/><!-- 配置Validator --><property name="validator" ref="validator"/></bean>
配置自定义校验器
<!-- 校验器 --><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器 --><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/><!-- 指定校验所使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties--><property name="validationMessageSource" ref="messageSource"/></bean><!-- 校验错误信息配置文件 --><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><!-- 资源文件名 --><property name="basenames"><list><value>classpath:CustomValidationMessages</value></list></property><!-- 资源文件编码方式 --><property name="fileEncodings" value="utf-8"/><!-- 对资源文件内容缓存时间,单位秒 --><property name="cacheSeconds" value="120"/></bean>

创建CustomValidationMessages

在classpath下新建CustomValidationMessages.properties

校验规则

需求:编辑商品信息提交时校验,商品生产日期不能为空,商品名称在1-30个字符之间
public class Items {    private Integer id;    //商品名称的长度限制在1到30个字符    @Size(min=1,max=30,message="{items.name.length.error}")    private String name;    private Float price;    private String pic;    //请输入商品生产日期    @NotNull(message="{items.createtime.is.notnull}")    private Date createtime;    ......}

捕获错误

需要修改controller方法,在需要校验的形参pojo前边加@Validated, 在pojo的后边加上参数BindingResult用来接收错误信息
@RequestMapping("/editItemSubmit")    //注意:每个校验pojo的前边必须加@Validated, 每个校验的pojo后边必须加BindingResult接收错误信息public String editItemSubmit(Model model,Integer id, @Validated @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {
错误信息输出
// 输出错误信息// 如果参数绑定时有错误if (bindingResult.hasErrors()) {// 获取错误List<ObjectError> errors = bindingResult.getAllErrors();// 准备在页面输出errors,页面使用jstl遍历model.addAttribute("errors",errors);for (ObjectError error : errors) {// 输出错误信息System.out.println(error.getDefaultMessage());}// 如果校验错误,回到商品修改页面return "editItem";}

在页面上展示错误

<!-- 错误信息 --><div style="color: red;"> <c:forEach items="${errors }" var="error">${error.defaultMessage }<br></c:forEach></div>
效果如下:

分组校验

需求:针对不同的controller方法通过分组校验达到个性化校验的目的,修改商品的修改功能,只校验商品的生产日期不能为空。
第一步:创建分组接口
package liuxun.ssm.controller.validation;/** * 校验分组: 用于商品修改的校验分组 * @author liuxun * */public interface ValidGroup1 {//接口不定义方法,就是只标识 哪些校验规则属于ValidGroup1分组}
第二步:定义校验规则属于哪个分组
 //请输入商品生产日期    //通过groups指定此校验属于哪个分组,可以指定多个分组 之间用逗号隔开groups={ValidGroup1.class,ValidGroup2.class}    @NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class})    private Date createtime;
第三步:在controller方法中使用定义的校验分组
public String editItemSubmit(Model model,Integer id, @Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {
运行如下:


统一异常处理

需求

一般项目中都需要做异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。
系统中的异常类型:
包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码中手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常。

统一异常处理解决方案

自定义异常

针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类。
CustomException:用于测试。
public class CustomException extends Exception {//异常信息private String message;public CustomException(String message) {super();this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}

异常处理

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

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

自定义异常处理器类

统一异常处理器实现HandlerExceptionResolver接口
public class CustomExceptionResolver implements HandlerExceptionResolver {//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {// 统一处理异常代码// ......return null;}}

配置统一异常处理类

<!-- 定义统一异常处理器类 --><bean class="liuxun.ssm.exception.CustomExceptionResolver"/>

异常处理逻辑

根据不同的异常类型进行异常处理。
系统自定义的异常类是CustomException,在Controller方法中、Service方法中手动抛出此类异常。
针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示。
针对非CustomException异常,对这类异常重新构造成一个CustomException,异常信息为"未知错误", 此类错误需要在系统测试阶段进行排除。
在统一异常处理器CustomExceptionResolver中实现上边逻辑。
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {//输出异常ex.printStackTrace();//统一处理异常代码//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面进行展示//异常信息String message = null;CustomException customException = null;//如果ex是自定义异常信息,直接取出异常信息if (ex instanceof CustomException) {customException = (CustomException) ex;} else {//针对非CustomException异常,对这类重新构成一个CustomException,异常信息为"未知错误"customException = new CustomException("未知错误");}//错误信息message = customException.getMessage();request.setAttribute("message", message);try {//转向到错误页面request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);} catch (ServletException | IOException e) {e.printStackTrace();}return new ModelAndView();}
展示错误信息页面
<body>${message }</body>

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

可以在controller方法、service方法、dao实现类中抛出异常,要求dao、service、controller遇到异常全部向上抛出异常,方法向上抛出异常throws Exception
public ItemsCustom findItemsById(int id) throws Exception {Items items = itemsMapper.selectByPrimaryKey(id);//如果查询的商品信息为空,抛出系统自定义异常if (items == null) {throw new CustomException("修改商品信息不存在");}// 在这里随着需求的变量,需要查询商品的其他相关信息,返回到controllerItemsCustom itemsCustom = new ItemsCustom();// 将items的属性拷贝到itemsCustomBeanUtils.copyProperties(items, itemsCustom);return itemsCustom;}
测试修改方法 参数改成一个不存在的值

流程图解如下:

RESTful支持

什么是RESTful

一、REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。
如果一个架构符合REST原则,就称它为RESTful架构。
要理解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动词,对服务器端资源进行操作,实现"表现层状态转化"。

六、误区
RESTful架构有一些典型的设计误区。
最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:
  POST /accounts/1/transfer/500/to/2
正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:
  POST /transaction HTTP/1.1
  Host: 127.0.0.1
  
  from=1&to=2&amount=500.00
另一个设计误区,就是在URI中加入版本号:
  http://www.example.com/app/1.0/foo
  http://www.example.com/app/1.1/foo
  http://www.example.com/app/2.0/foo
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分
  Accept: vnd.example-com.foo+json; version=1.0
  Accept: vnd.example-com.foo+json; version=1.1
  Accept: vnd.example-com.foo+json; version=2.0

url的RESTful实现

非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方式返回。

第一步更改DispatcherServlet配置

在web.xml中添加如下配置
<!-- restful配置 --><servlet><servlet-name>springmvc_rest</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml  --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc_rest</servlet-name><!-- rest方式配置为/ --><url-pattern>/</url-pattern></servlet-mapping>

第二步参数通过url传递

//根据id查看商品信息rest接口 //@RequestMapping中指定restful方式的url参数,参数要使用{}包起来 //@PathVariable将url中的{}包起来的参数和形参进行绑定  @RequestMapping("/viewItems/{id}")  public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{  //调用Service查询商品信息  ItemsCustom itemsCustom = itemsService.findItemsById(id);  return itemsCustom;  }

第三步设置静态资源解析

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

需要在springmvc.xml中通过设置对静态资源解析
<!-- 静态资源解析 --><mvc:resources location="/js/" mapping="/js/**"/><mvc:resources location="/img/" mapping="/img/**"/>
访问/js/**的url从工程下/js/下解析
测试结果:

注意:使用<mvc:annotation-driven/>可以替代注解映射器和注解适配器的配置,而且原先需要向注解适配器中注入webBinder(包含validator校验器以及converter参数转换器或者属性编辑器) 使用<mvc:annotation-driven/>替代后不用webBinder 可以直接设置到<mvc:annotation-driven/>标签的节点属性中,如下所示:

springmvc拦截器

拦截器的通常场合

用户请求到DIspatcherServlet中,DispatcherServlet调用HandlerMapping查找Handler,HandlerMapping返回一个拦截器的链,springmvc的拦截器是通过HandlerMapping发起的。
在企业开发中,使用拦截器实现用户认证(用户登陆后进行身份校验拦截器),用户权限拦截。

springmvc拦截器方法

自定义拦截器,需要实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor{//在执行handler之前执行的//用于用户认证校验、用户权限校验@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        System.out.println("HandlerInterceptor1...preHandle");//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}//在执行handler返回modelAndView之前执行//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {System.out.println("HandlerInterceptor1...postHandle");}//执行handler之后执行此方法//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长//实现系统,统一日志记录@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)throws Exception {System.out.println("HandlerInterceptor1...afterCompletion");}}

测试拦截器

定义两个拦截器


配置拦截器

配置全局拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping映射器。
在springmvc.xml中配置:
<!-- 拦截器 --><mvc:interceptors><!-- 多个拦截器,顺序执行 --><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean></mvc:interceptor></mvc:interceptors>

测试1:1号和2号都放行

测试结果
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle

HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle

HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
执行preHandler是顺序执行的
执行postHandler、afterHandler是倒序执行的

测试2:1号放行,2号不放行

HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
如果当前拦截器的preHandler不放行,postHandler、afterHandler都不放行。
拦截器链中只要有一个拦截器不放行,所有拦截器的postHandler都不会执行,controller就不能执行完成

测试3:1号和2号都不放行

测试结果:
HandlerInterceptor1...preHandle
总结:
只有前边的拦截器preHandler方法放行,下边的拦截器的preHandler才会执行

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

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

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

需求

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

用户登录及退出功能开发

@Controllerpublic class LoginController {//用户登录提交方法@RequestMapping("/login")public String login(HttpSession session,String usercode,String password) throws Exception{//调用service校验用户账号和密码的正确性//...//如果Service校验通过,将用户身份记录到sessionsession.setAttribute("usercode", usercode);//重定向到商品查询页面return "redirect:/items/queryItems.action";}//用户退出@RequestMapping("/logout")public String logout(HttpSession session) throws Exception{//session失效session.invalidate();//重定向到商品查询页面return "redirect:/items/queryItems.action";}}

用户身份认证拦校验截器

拦截器实现思路:http请求URL,如果URL是公开地址(不需要认证即可访问的URL) 放行,如果用户在Session中存在 放行,如果用户在Session中不存在,跳转到登录页面。

自定义登录拦截器

@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        //得到请求的urlString url = request.getRequestURI();//判断是否是公开地址//实际开发中需要将公开地址配置在配置文件中//...if (url.indexOf("login.action")>=0) {//如果是公开地址 则放行return true;}//判断用户身份在Session中是否存在HttpSession session = request.getSession();String usercode = (String) session.getAttribute("usercode");//如果用户身份在session中存在则放行if (usercode!=null) {return true;}//执行到这里拦截,跳转到登录页面,用户进行身份认证request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}
在springmvc.xml中配置拦截器
    <mvc:interceptors>    <mvc:interceptor>   <!-- /**可以拦截多层路径 -->   <mvc:mapping path="/**"/>   <bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>    </mvc:interceptor></mvc:interceptors>
效果如下:


实例源代码如下

源码已经上传GitHub  https://github.com/LX1993728/springmvc_mybatis_1
工程目录结构如下:




web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID" version="2.5"><display-name>springmvc_mybatis_1</display-name><!-- 配置Spring容器监听器 --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 前端控制器 --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml  --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- 可以配置/ ,此工程 所有请求全部由springmvc解析,此种方式可以实现 RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析 可以配置*.do或*.action,所有请求的url扩展名为.do或.action由springmvc解析,此种方法常用 不可以/*,如果配置/*,返回jsp也由springmvc解析,这是不对的。 --><url-pattern>*.action</url-pattern></servlet-mapping><!-- restful配置 --><servlet><servlet-name>springmvc_rest</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml  --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc_rest</servlet-name><!-- rest方式配置为/ --><url-pattern>/</url-pattern></servlet-mapping><!-- post乱码处理 --><filter><filter-name>CharacterEncodingFilter</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></filter><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app>
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- 定义别名 --><typeAliases> <!-- 批量别名定义 指定包路径,自动扫描包下边的pojo, 定义别名,别名默认为类名(首字母小写或大写) --><package name="liuxun.ssm.po" /></typeAliases><!-- 由于使用Spring和mybatis整合的mapper扫描器,这里可以不用配置了 <mappers><package name="liuxun.ssm.mapper"/></mappers>--></configuration>
applicationContext-dao.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:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 加载配置文件 --><context:property-placeholder location="classpath:db.properties" /><!-- 数据库连接池 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">      <property name="driverClassName" value="${jdbc.driver}"/>      <property name="url" value="${jdbc.url}"/>      <property name="username" value="${jdbc.username}"/>      <property name="password" value="${jdbc.password}"/>      <property name="maxActive" value="10"/>      <property name="maxIdle" value="5"/></bean><!-- SqlSessionFactory -->    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    <!-- 数据源 -->    <property name="dataSource" ref="dataSource"/>    <!-- mybatis配置文件 -->    <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>    </bean>        <!--     MapperScannerConfigurer: mapper扫描器,将包下边的mapper接口自动创建代理对象,    自动创建到Spring容器中,bean的id是mapper的类名(首字母小写)     -->     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">     <!-- 配置扫描包的路径     如果要扫描多个包,中间使用半角逗号隔开     要求mapper.xml和mapper.java同名且在同一目录      -->     <property name="basePackage" value="liuxun.ssm.mapper"/>     <!-- 使用SqlSessionFactoryBeanName -->     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>     </bean></beans>
applicationContext-service.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:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">         <!-- 商品管理的Service -->   <bean  id="itemsService" class="liuxun.ssm.service.impl.ItemsServiceImpl"></bean></beans>
applicationContext-transaction.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:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 使用声明式事务,可以有效规范代码 --><!-- 事务管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRED" /><tx:method name="insert*" propagation="REQUIRED" /><tx:method name="update*" propagation="REQUIRED" /><tx:method name="delete*" propagation="REQUIRED" /><tx:method name="find*" propagation="SUPPORTS" read-only="true" /><tx:method name="select*" propagation="SUPPORTS" read-only="true" /><tx:method name="get*" propagation="SUPPORTS" read-only="true" /></tx:attributes></tx:advice><!-- aop --><aop:config><aop:advisor advice-ref="txAdvice" pointcut="execution(* liuxun.ssm.service.impl.*.*(..))" /></aop:config></beans>
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:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 使用spring组件扫描 --><context:component-scan base-package="liuxun.ssm.controller" /><!-- 静态资源解析 --><mvc:resources location="/js/" mapping="/js/**"/><mvc:resources location="/img/" mapping="/img/**"/><!-- 通过使用mvc的annotation-driven 可以替代下边的处理器映射器和适配器 --><!--  <mvc:annotation-driven conversion-service="conversionService" > </mvc:annotation-driven> --><!-- 注解处理器映射器 --><beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>    <!-- 注解适配器 -->    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">    <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 -->    <property name="webBindingInitializer" ref="customBinder"/>    <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 -->    <property name="messageConverters">    <list>    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>    </list>    </property>    </bean>    <!-- 配置视图解析器 要求将jstl的包加到classpath --><!-- ViewResolver --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- 自定义webBinder --><bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"><!-- 配置Validator --><property name="validator" ref="validator"/><!-- 使用converter进行参数转换 --><property name="conversionService" ref="conversionService"/><!-- propertyEditorRegistrars用于属性编辑器 --><!--  <property name="propertyEditorRegistrars"><list><ref bean="customPropertyEditor"/></list></property>--></bean><!-- 注册属性编辑器 --><bean id="customPropertyEditor" class="liuxun.ssm.controller.propertyeditor.CustomPropertyEditor"/><!-- 注册转换器 --><bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="converters"><list><bean class="liuxun.ssm.controller.converter.CustomDateConverter"/><bean class="liuxun.ssm.controller.converter.StringTrimConverter"/></list></property></bean><!-- 拦截器 --><mvc:interceptors><!-- 多个拦截器,顺序执行 -->   <!-- <mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean></mvc:interceptor>  -->    <mvc:interceptor>   <!-- /**可以拦截多层路径 -->   <mvc:mapping path="/**"/>   <bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>    </mvc:interceptor></mvc:interceptors><!-- 文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置上传文件的最大尺寸为5MB --><property name="maxUploadSize"><value>5242880</value></property></bean><!-- 定义统一异常处理器类 --><bean class="liuxun.ssm.exception.CustomExceptionResolver"/><!-- 校验器 --><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器 --><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/><!-- 指定校验所使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties--><property name="validationMessageSource" ref="messageSource"/></bean><!-- 校验错误信息配置文件 --><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><!-- 资源文件名 --><property name="basenames"><list><value>classpath:CustomValidationMessages</value></list></property><!-- 资源文件编码方式 --><property name="fileEncodings" value="utf-8"/><!-- 对资源文件内容缓存时间,单位秒 --><property name="cacheSeconds" value="120"/></bean></beans>
CustomValidationMessages.properties

db.properties
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatisjdbc.username=rootjdbc.password=root
log4j.properties
# Global logging configuration\uff0c\u5efa\u8bae\u5f00\u53d1\u73af\u5883\u4e2d\u8981\u7528debuglog4j.rootLogger=DEBUG, stdout# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
ItemsController.java
package liuxun.ssm.controller;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.propertyeditors.CustomDateEditor;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.validation.BindingResult;import org.springframework.validation.ObjectError;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.servlet.ModelAndView;import liuxun.ssm.controller.validation.ValidGroup1;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;import liuxun.ssm.service.ItemsService;/** * 商品管理 *  * @author liuxun * */@Controller// 定义url的根路径,访问时 根路径+方法的url@RequestMapping("/items")public class ItemsController {// 注入Service@Autowiredprivate ItemsService itemsService;// 单独将商品类型的方法提出来,将方法返回值填充到request,在页面显示@ModelAttribute("itemsType")public Map<String, String> getItemsType() throws Exception{HashMap<String, String> itemsType = new HashMap<String, String>();itemsType.put("001", "数码");itemsType.put("002", "服装");return itemsType;}// 查询商品信息方法@RequestMapping("/queryItems")public ModelAndView queryItems(HttpServletRequest request) throws Exception {// 调用Service查询商品列表List<ItemsCustom> itemsList = itemsService.findItemsList(null);ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("itemsList", itemsList);// 指定逻辑视图名modelAndView.setViewName("itemsList");return modelAndView;}// 批量修改商品查询@RequestMapping("/editItemsList")public ModelAndView editItemsList(HttpServletRequest request) throws Exception {// 调用Service查询商品列表List<ItemsCustom> itemsList = itemsService.findItemsList(null);ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("itemsList", itemsList);// 指定逻辑视图名modelAndView.setViewName("editItemsList");return modelAndView;}// 批量修改商品提交@RequestMapping("/editItemsListSubmit")    public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){return "success";}// 商品修改页面// 使用method=RequestMethod.GET限制使用get方法/*  @RequestMapping(value="/editItems",method={RequestMethod.GET}) public  ModelAndView editItems() throws Exception{    ModelAndView modelAndView = new ModelAndView();    // 调用Service查询商品信息 ItemsCustom itemsCustom =  itemsService.findItemsById(1); //将模型数据传到jsp  modelAndView.addObject("item", itemsCustom); // 指定逻辑视图名  modelAndView.setViewName("editItem");    return modelAndView; } */// 方法返回字符串,字符串就是逻辑视图名,Model作用就是将数据填充到request域,在页面展示  @RequestMapping(value="/editItems",method={RequestMethod.GET})   public String editItems(Model model,Integer id) throws Exception{   // 将id传递到页面  model.addAttribute("id",id);    // 调用Service查询商品信息   ItemsCustom itemsCustom = itemsService.findItemsById(id); //将模型数据传到jsp   model.addAttribute("item",itemsCustom);    //return "editItem_2";  return "editItem";  }  //根据id查看商品信息rest接口 //@RequestMapping中指定restful方式的url参数,参数要使用{}包起来 //@PathVariable将url中的{}包起来的参数和形参进行绑定  @RequestMapping("/viewItems/{id}")  public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{  //调用Service查询商品信息  ItemsCustom itemsCustom = itemsService.findItemsById(id);  return itemsCustom;  }// 方法返回void/*@RequestMapping(value = "/editItems", method = { RequestMethod.GET })public void editItems(HttpServletRequest request, HttpServletResponse response,// @RequestParam(value="item_id",required=false,defaultValue="1")Integer idInteger id) throws Exception {// 调用Service查询商品信息ItemsCustom itemsCustom = itemsService.findItemsById(id);request.setAttribute("item", itemsCustom);// 注意如果使用request转向页面,这里指定页面的完整路径request.getRequestDispatcher("/WEB-INF/jsp/editItem.jsp").forward(request, response);}*/// 商品修改提交// itemsQueryVo是包装类型的pojo@RequestMapping("/editItemSubmit")// public String editItemSubmit(Integer id,ItemsCustom// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{    //注意:每个校验pojo的前边必须加@Validated, 每个校验的pojo后边必须加BindingResult接收错误信息public String editItemSubmit(Model model,Integer id, @Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {// 输出错误信息// 如果参数绑定时有错误if (bindingResult.hasErrors()) {// 获取错误List<ObjectError> errors = bindingResult.getAllErrors();// 准备在页面输出errors,页面使用jstl遍历model.addAttribute("errors",errors);for (ObjectError error : errors) {// 输出错误信息System.out.println(error.getDefaultMessage());}// 如果校验错误,回到商品修改页面return "editItem";}// 进行数据回显model.addAttribute("id", id);//model.addAttribute("item",itemsCustom);//进行图片上传if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {// 图片上传成功后,将图片的地址写到数据库String filePath = "/Users/liuxun/Desktop/pictures";// 上传文件原始名称String originFileName = pictureFile.getOriginalFilename();// 新的图片的名称String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));// 新文件File file = new File(filePath+File.separator+newFileName);// 将内存中的文件写入磁盘pictureFile.transferTo(file);// 图片上传成功,将图片地址写入数据库itemsCustom.setPic(newFileName);}// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 提交后回到修改页面return "editItem";// 请求重定向//return "redirect:queryItems.action";// 转发// return "forward:queryItems.action";}// 删除商品@RequestMapping("/deleteItems")public String deleteItems(Integer[] delete_id) throws Exception{System.out.println(delete_id);return "success";}//自定义属性编辑器/*@InitBinderpublic void initBinder(WebDataBinder binder) throws Exception {// Date.class必须是与controler方法形参pojo属性一致的date类型,这里是java.util.Datebinder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));}*/}
JsonTest.java
package liuxun.ssm.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import liuxun.ssm.po.ItemsCustom;/** * json测试 * @author liuxun * */@Controllerpublic class JsonTest {//请求的json响应json,请求商品信息,商品信息使用json格式,输出商品信息@RequestMapping("/requestJson")public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{return itemsCustom;}@RequestMapping("/responseJson")public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{return itemsCustom;}}
LoginController.java
package liuxun.ssm.controller;import javax.servlet.http.HttpSession;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;/** * 登录和退出 * @author liuxun * */@Controllerpublic class LoginController {//用户登录提交方法@RequestMapping("/login")public String login(HttpSession session,String usercode,String password) throws Exception{//调用service校验用户账号和密码的正确性//...//如果Service校验通过,将用户身份记录到sessionsession.setAttribute("usercode", usercode);//重定向到商品查询页面return "redirect:/items/queryItems.action";}//用户退出@RequestMapping("/logout")public String logout(HttpSession session) throws Exception{//session失效session.invalidate();//重定向到商品查询页面return "redirect:/items/queryItems.action";}}
CustomDateConverter.java
package liuxun.ssm.controller.converter;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import org.springframework.core.convert.converter.Converter;/** * 自定义日期转换器 * @author liuxun * */public class CustomDateConverter implements Converter<String, Date> {@Overridepublic Date convert(String source) {if (source!=null&&source.trim().length()>0) {// 进行日期转换try {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);} catch (ParseException e) {e.printStackTrace();}}return null;}}
StringTrimConverter.java
package liuxun.ssm.controller.converter;import org.springframework.core.convert.converter.Converter;/** * 自定义去除字符串前后空格的转换器 * @author liuxun * */public class StringTrimConverter implements Converter<String, String>{@Overridepublic String convert(String source) {//去掉字符串两边空格,如果去除后为空设置为nullif (source!=null) {source = source.trim();if (source.equals("")) {return null;}}return source;}}
LoginInterceptor.java
package liuxun.ssm.controller.interceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;/** * 测试拦截器1 * @author liuxun * */public class LoginInterceptor implements HandlerInterceptor{//在执行handler之前执行的//用于用户认证校验、用户权限校验@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        //得到请求的urlString url = request.getRequestURI();//判断是否是公开地址//实际开发中需要将公开地址配置在配置文件中//...if (url.indexOf("login.action")>=0) {//如果是公开地址 则放行return true;}//判断用户身份在Session中是否存在HttpSession session = request.getSession();String usercode = (String) session.getAttribute("usercode");//如果用户身份在session中存在则放行if (usercode!=null) {return true;}//执行到这里拦截,跳转到登录页面,用户进行身份认证request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}//在执行handler返回modelAndView之前执行//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {System.out.println("HandlerInterceptor2...postHandle");}//执行handler之后执行此方法//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长//实现系统,统一日志记录@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)throws Exception {System.out.println("HandlerInterceptor2...afterCompletion");}}
CustomPropertyEditor.java
package liuxun.ssm.controller.propertyeditor;import java.text.SimpleDateFormat;import java.util.Date;import org.springframework.beans.PropertyEditorRegistrar;import org.springframework.beans.PropertyEditorRegistry;import org.springframework.beans.propertyeditors.CustomDateEditor;/** * 自定义属性编辑器 * @author liuxun * */public class CustomPropertyEditor implements PropertyEditorRegistrar {public void registerCustomEditors(PropertyEditorRegistry binder) {binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));}}
ValidGroup1.java
package liuxun.ssm.controller.validation;/** * 校验分组: 用于商品修改的校验分组 * @author liuxun * */public interface ValidGroup1 {//接口不定义方法,就是只标识 哪些校验规则属于ValidGroup1分组}
CustomException.java
package liuxun.ssm.exception;public class CustomException extends Exception {//异常信息private String message;public CustomException(String message) {super();this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
CustomExceptionResolver.java
package liuxun.ssm.exception;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerExceptionResolver;import org.springframework.web.servlet.ModelAndView;public class CustomExceptionResolver implements HandlerExceptionResolver {//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {//输出异常ex.printStackTrace();//统一处理异常代码//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面进行展示//异常信息String message = null;CustomException customException = null;//如果ex是自定义异常信息,直接取出异常信息if (ex instanceof CustomException) {customException = (CustomException) ex;} else {//针对非CustomException异常,对这类重新构成一个CustomException,异常信息为"未知错误"customException = new CustomException("未知错误");}//错误信息message = customException.getMessage();request.setAttribute("message", message);try {//转向到错误页面request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);} catch (ServletException | IOException e) {e.printStackTrace();}return new ModelAndView();}}
ItemsMapperCustom.java
package liuxun.ssm.mapper;import java.util.List;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;/** * 商品自定义Mapper * @author liuxun * */public interface ItemsMapperCustom {  // 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;}
ItemsMapperCustom.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="liuxun.ssm.mapper.ItemsMapperCustom">  <!-- 商品查询的sql片段  建议以单表为单位定义查询条件  建议将常用的查询条件都写出来   -->   <sql id="query_items_where">     <if test="itemsCustom!=null">     <if test="itemsCustom.name!=null and itemsCustom.name.trim().length >0">      and name like '%${itemsCustom.name.trim()}%'     </if>     <if test="itemsCustom.id!=null">     and id = #{itemsCustom.id}     </if>     </if>   </sql>      <!-- 商品查询   parameterType:输入查询条件    -->  <select id="findItemsList" parameterType="liuxun.ssm.po.ItemsQueryVo" resultType="liuxun.ssm.po.ItemsCustom">  SELECT * FROM items  <where>  <include refid="query_items_where"/>  </where>  </select>  </mapper>
ItemsCustom.java
package liuxun.ssm.po;/** * 商品信息的扩展类 * @author liuxun * */public class ItemsCustom extends Items {}
ItemsQueryVo.java
/** * 商品的包装类 * @author liuxun * */public class ItemsQueryVo {// 商品信息private ItemsCustom itemsCustom;// 定义一个Listprivate List<ItemsCustom> itemsList;public ItemsCustom getItemsCustom() {return itemsCustom;}public void setItemsCustom(ItemsCustom itemsCustom) {this.itemsCustom = itemsCustom;}public List<ItemsCustom> getItemsList() {return itemsList;}public void setItemsList(List<ItemsCustom> itemsList) {this.itemsList = itemsList;}}
修改自动生成的Items 添加校验
public class Items {    private Integer id;    //商品名称的长度限制在1到30个字符    @Size(min=1,max=30,message="{items.name.length.error}")    private String name;    private Float price;    private String pic;    //请输入商品生产日期    //通过groups指定此校验属于哪个分组,可以指定多个分组 之间用逗号隔开groups={ValidGroup1.class,ValidGroup2.class}    @NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class})    private Date createtime;    ....    ....}
ItemsService
package liuxun.ssm.service;import java.util.List;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;/** * 商品Service接口 *  * @author liuxun * */public interface ItemsService {// 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;// 根据商品id查询商品信息public ItemsCustom findItemsById(int id) throws Exception;// 更新商品信息/** * 定义Service接口,遵循单一职责,将业务参数细化(不要使用包装类型,比如Map) * @param id  修改商品的id * @param itemsCustom  修改商品的信息 * @throws Exception */public void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception;}
ItemsServiceImpl.java
package liuxun.ssm.service.impl;import java.util.List;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import liuxun.ssm.exception.CustomException;import liuxun.ssm.mapper.ItemsMapper;import liuxun.ssm.mapper.ItemsMapperCustom;import liuxun.ssm.po.Items;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;import liuxun.ssm.service.ItemsService;public class ItemsServiceImpl implements ItemsService {// 注入mapper@Autowiredprivate ItemsMapperCustom itemsMapperCustom;@Autowiredprivate ItemsMapper itemsMapper;// 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {return itemsMapperCustom.findItemsList(itemsQueryVo);}public ItemsCustom findItemsById(int id) throws Exception {Items items = itemsMapper.selectByPrimaryKey(id);//如果查询的商品信息为空,抛出系统自定义异常if (items == null) {throw new CustomException("修改商品信息不存在");}// 在这里随着需求的变量,需要查询商品的其他相关信息,返回到controllerItemsCustom itemsCustom = new ItemsCustom();// 将items的属性拷贝到itemsCustomBeanUtils.copyProperties(items, itemsCustom);return itemsCustom;}public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {// 写业务代码// 对于关键业务数据的非空校验if (id == null) {// 抛出异常,提示调用接口的用户,id不能为空// ...}itemsMapper.updateByPrimaryKeySelective(itemsCustom);}}
itemsList.jsp
<%@ 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"  prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>查询商品列表</title><script type="text/javascript">function deleteItems(){  // 将form的action指向删除商品的地址  document.itemsForm.action = "${pageContext.request.contextPath }/items/deleteItems.action";  // 进行form提交  document.itemsForm.submit();}</script></head><body> 当前用户:${usercode }<c:if test="${usercode != null}"><a href="${pageContext.request.contextPath}/logout.action">退出</a></c:if><form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post">查询条件:<table width="100%" border=1><tr><td>商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select></td></tr>  <tr><td><input type="submit" value="查询"/><input type="button" value="批量删除" onclick="deleteItems()"/></td></tr></table>商品列表:<table width="100%" border=1><tr><td>选择</td><td>商品名称</td><td>商品价格</td><td>生产日期</td><td>商品描述</td><td>操作</td></tr><c:forEach items="${itemsList }" var="item"><tr>    <td><input type="checkbox" name="delete_id" value="${item.id }"/></td><td>${item.name }</td><td>${item.price }</td><td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td><td>${item.detail }</td><td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td></tr></c:forEach></table></form></body></html>
editItem.jsp
<%@ 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"  prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>修改商品信息</title></head><body> <!-- 错误信息 --><div style="color: red;"><c:forEach items="${errors }" var="error">${error.defaultMessage }<br></c:forEach></div><form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data"><input type="hidden" name="id" value="${id }"/>修改商品信息:<table width="100%" border=1><tr><td>商品名称</td><td><input type="text" name="name" value="${item.name }"/></td></tr><tr><td>商品价格</td><td><input type="text" name="price" value="${item.price }"/></td></tr><tr><td>商品生产日期</td><td><input type="text" name="createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td></tr><tr><td>商品图片</td><td><c:if test="${item.pic != null }"><img src="/pic/${item.pic}" width="100px" height="100px"/><br/></c:if><input type="file" name="pictureFile"/></td></tr><tr><td>商品简介</td><td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"/></td></tr></table></form></body></html>
editItemsList.jsp
<%@ 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"  prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>查询商品列表</title><script type="text/javascript">function updateItems(){  // 将form的action指向修改商品的地址  document.itemsForm.action = "${pageContext.request.contextPath }/items/editItemsListSubmit.action";  // 进行form提交  document.itemsForm.submit();}</script></head><body> <form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post" >查询条件:<table width="100%" border=1><tr><td>商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select></td></tr>  <tr><td><input type="submit" value="查询"/><input type="button" value="批量修改提交" onclick="updateItems()"/></td></tr></table>商品列表:<table width="100%" border=1><tr><td>商品名称</td><td>商品价格</td><td>生产日期</td><td>商品描述</td><td>操作</td></tr><c:forEach items="${itemsList }" var="item" varStatus="s"><tr><td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td><td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"/></td><td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td><td>${item.detail }</td><td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td></tr></c:forEach></table></form></body></html>
jsontest.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>json测试</title><script type="text/javascript"src="${pageContext.request.contextPath}/js/jquery-1.4.4.min.js"></script><script type="text/javascript">// 请求json响应jsonfunction requestJson(){$.ajax({url:"${pageContext.request.contextPath}/requestJson.action",type:"post",contentType:"application/json;charset=utf-8",//请求json数据 使用json表示商品信息data:'{"name":"手机","price":1999}',success:function(data){alert(data.name);}});}// 请求key/value响应jsonfunction responseJson(){$.ajax({url:"${pageContext.request.contextPath}/responseJson.action",type:"post",//contentType:"application/json;charset=utf-8",//请求key/value数据 使用地址拼接表示商品信息data:"name=手机&price=1999",success:function(data){alert(data.name);}});}</script></head><body><input type="button" value="请求json响应json" onclick="requestJson()"/><input type="button" value="请求key/value响应json" onclick="responseJson()"/></body></html>


阅读全文
4 0
原创粉丝点击