springmvc教程(4)

本文深入探讨SpringMVC框架的高级功能,包括文件上传、数据校验、异常处理、JSON交互、RESTful支持等内容,并提供了详细的代码示例。

springmvc教程系列

springmvc史上最好教程(2)

springmvc史上最好教程(1)

springmvc史上最好教程(3)


2注解开发-高级

2.1上传图片

2.1.1配置虚拟目录

2.1.2配置解析器

<!-- 文件上传 -->

<bean id="multipartResolver"

class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

<!-- 设置上传文件的最大尺寸为5MB -->

<property name="maxUploadSize">

<value>5242880</value>

</property>

</bean>


2.1.3jar

CommonsMultipartResolver解析器依赖commons-fileuploadcommons-io,加入如下jar包:

2.1.4单个图片上传

1、controller

//商品修改提交

@RequestMapping("/editItemSubmit")

public String editItemSubmit(Items items, MultipartFile pictureFile)throws Exception{

//原始文件名称

String pictureFile_name =  pictureFile.getOriginalFilename();

//新文件名称

String newFileName = UUID.randomUUID().toString()+pictureFile_name.substring(pictureFile_name.lastIndexOf("."));

//上传图片

File uploadPic = new java.io.File("F:/develop/upload/temp/"+newFileName);

if(!uploadPic.exists()){

uploadPic.mkdirs();

}

//向磁盘写文件

pictureFile.transferTo(uploadPic);

 

.....

 


2、 页面:

form添加enctype="multipart/form-data"

<form id="itemForm"

action="${pageContext.request.contextPath }/item/editItemSubmit.action"

method="post" enctype="multipart/form-data">

<input type="hidden" name="pic" value="${item.pic }" />

 


filenamecontroller形参一致:

<tr>

<td>商品图片</td>

<td><c:if test="${item.pic !=null}">

<img src="/pic/${item.pic}" width=100 height=100 />

<br />

</c:if> <input type="file" name="pictureFile" /></td>

</tr>


2.2Validation(了解)

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

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

2.2.1需求

对商品信息进行校验,是否必须,输入数据合法性。

2.2.2加入jar

2.2.3配置validator

<!-- 校验错误信息配置文件 -->

<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>

<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>


2.2.4validator加到处理器适配器

配置方式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>


2.2.5添加验证规则

public class Items {

    private Integer id;

    @Size(min=1,max=30,message="{item.name.length.illigel}")

    private String name;

    

    @NotEmpty(message="{pic.is.null}")

    private String pic;

 


2.2.6错误消息文件CustomValidationMessages

item.name.length.illigel=商品在名称在13个字符之间

pic.is.null=请上传图片

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

2.2.7捕获错误

修改Controller方法:

// 商品修改提交

@RequestMapping("/editItemSubmit")

public String editItemSubmit(@Validated @ModelAttribute("item") Items items,BindingResult result,

@RequestParam("pictureFile") MultipartFile[] pictureFile,Model model)

throws Exception {

//如果存在校验错误则转到商品修改页面

if (result.hasErrors()) {

List<ObjectError> errors = result.getAllErrors();

for(ObjectError objectError:errors){

System.out.println(objectError.getCode());

System.out.println(objectError.getDefaultMessage());

}

return "item/editItem";

}

 

 


注意:添加@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" %>


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

<spring:hasBindErrors name="item">

<c:forEach items="${errors.allErrors}" var="error">

${error.defaultMessage }<br/>

</c:forEach>

</spring:hasBindErrors>

 


说明:

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

2.2.8分组校验

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

定义分组:

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

public interface ValidGroup1 {

 

}

public interface ValidGroup2 {

 

}


指定分组校验:

public class Items {

    private Integer id;

//这里指定分组ValidGroup1,此@Size校验只适用ValidGroup1校验

    @Size(min=1,max=30,message="{item.name.length.illigel}",groups={ValidGroup1.class})

    private String name;

 

// 商品修改提交

@RequestMapping("/editItemSubmit")

public String editItemSubmit(@Validated(value={ValidGroup1.class}) @ModelAttribute("item") Items items,BindingResult result,

@RequestParam("pictureFile") MultipartFile[] pictureFile,Model model)

throws Exception {


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

@Validated(value={ValidGroup1.classValidGroup2.class})

2.3异常处理器

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

2.3.1异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

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

2.3.2自定义异常类

为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controllerservicedao抛出此类异常说明是系统预期处理的异常信息。

	public class CustomException extends Exception {

 

/** serialVersionUID*/

private static final long serialVersionUID = -5212079010855161498L;

public CustomException(String message){

super(message);

this.message = message;

}

 

//异常信息

private String message;

 

public String getMessage() {

return message;

}

 

public void setMessage(String message) {

this.message = message;

}

}

 

 


2.3.3自定义异常处理器

public class CustomExceptionResolver implements HandlerExceptionResolver {

 

@Override

public ModelAndView resolveException(HttpServletRequest request,

HttpServletResponse response, Object handler, Exception ex) {

 

ex.printStackTrace();

 

CustomException customException = null;

//如果抛出的是系统自定义异常则直接转换

if(ex instanceof CustomException){

customException = (CustomException)ex;

}else{

//如果抛出的不是系统自定义异常则重新构造一个未知错误异常。

customException = new CustomException("未知错误,请与系统管理 员联系!");

}

ModelAndView modelAndView = new ModelAndView();

modelAndView.addObject("message", customException.getMessage());

modelAndView.setViewName("error");

 

return modelAndView;

}

 

}

 


2.3.4错误页面

 

<%@ 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>

您的操作出现错误如下:<br/>

${message }

</body>

 

</html>

 

 

 


2.3.5异常处理器配置

springmvc.xml中添加:

<!-- 异常处理器 -->

<bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>


2.3.6异常测试

修改商品信息,id输入错误提示商品信息不存在。

修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常:

// 调用service查询商品信息

Items item = itemService.findItemById(id);

if(item == null){

throw new CustomException("商品信息不存在!");

}

 

 


请自行实现在servicedao中跑出异常。

2.4json数据交互

2.4.1@RequestBody

作用:

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

本例子应用:

@RequestBody注解实现接收http请求的json数据,将json数据转换为java对象

2.4.2@ResponseBody

作用:

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

本例子应用:

@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端

2.4.3请求json,响应json实现:

2.4.3.1环境准备

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

2.4.3.2配置json转换器

在注解适配器中加入messageConverters

<!--注解适配器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">

<property name="messageConverters">

<list>

<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>

</list>

</property>

</bean>


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

2.4.3.3controller编写
// 商品修改提交json信息,响应json信息

@RequestMapping("/editItemSubmit_RequestJson")

public @ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items) throws Exception {

System.out.println(items);

//itemService.saveItem(items);

return items;

 

}


2.4.3.4页面js方法编写:

引入 js

<script type="text/javascript"

src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>

//请求json响应json

function request_json(){

$.ajax({

type:"post",

url:"${pageContext.request.contextPath }/item/editItemSubmit_RequestJson.action",

contentType:"application/json;charset=utf-8",

data:'{"name":"测试商品","price":99.9}',

success:function(data){

alert(data);

}

});

}

 


2.4.3.5测试结果:

从上图可以看出请求的数据是json格式

2.4.4Form提交,响应json实现:

采用form提交是最常用的作法,通常有postget两种方法,响应json数据是为了方便客户端处理,实现如下:

2.4.4.1环境准备

同第一个例子

2.4.4.2controller编写

// 商品修改提交,提交普通form表单数据,响应json

@RequestMapping("/editItemSubmit_ResponseJson")

public @ResponseBody Items editItemSubmit_ResponseJson(Items items) throws Exception {

 

System.out.println(items);

 

//itemService.saveItem(items);

return items;

}


2.4.4.3页面js方法编写:

function formsubmit(){

var user = " name=测试商品&price=99.9";

alert(user);

  $.ajax(

{

type:'post',//这里改为get也可以正常执行

url:'${pageContext.request.contextPath}/item/ editItemSubmit_RequestJson.action',

//ContentType没指定将默认为:application/x-www-form-urlencoded

data:user,

success:function(data){

alert(data.name);

}

}

)

}


从上边的js代码看出,已去掉ContentType的定义,ContentType默认为:application/x-www-form-urlencoded格式。

2.4.4.4测试结果:

从上图可以看出请求的数据是标准的key/value格式。

2.4.4.5jqueryform插件插件

针对上边第二种方法,可以使用jqueryform插件提交form表单,实现ajax提交form表单,如下:

引用js

<script type="text/javascript"

src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>

<script type="text/javascript"

src="${pageContext.request.contextPath }/js/jquery.form.min.js"></script>

js方法如下:

function response_json() {

//form对象

var formObj = $("#itemForm");

//执行ajax提交

formObj.ajaxSubmit({

dataType : "json",//设置预期服务端返回json

success : function(responseText) {

alert(responseText);

}

});

}

 


2.4.5小结

实际开发中常用第二种方法,请求key/value数据,响应json结果,方便客户端对结果进行解析。

2.5RESTful支持

2.5.1需求

RESTful方式商品修改、商品查询。

2.5.2添加DispatcherServletrest配置

<servlet>

<servlet-name>springmvc-servlet-rest</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring/springmvc.xml</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>springmvc-servlet-rest</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


2.5.3URL模板模式映射

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

@RequestMapping("/ editItem/{item_id}") 

public String useredit(@PathVariable("item_id ") String id,Model model) throws Exception{

//方法中使用@PathVariable获取useried的值,使用model传回页面

model.addAttribute("userid", userid);

return"/user/useredit";

}


如果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;

}


2.5.4静态资源访问<mvc:resources>

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

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

<mvc:resources location="/js/" mapping="/js/**"/>

3拦截器

3.1定义

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

3.2拦截器定义

实现HandlerInterceptor接口,如下:

Public class HandlerInterceptor1 implements HandlerInterceptor{

 

/**

 * controller执行前调用此方法

 * 返回true表示继续执行,返回false中止执行

 * 这里可以加入登录校验、权限拦截等

 */

@Override

Public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

// TODO Auto-generated method stub

Return false;

}

/**

 * controller执行后但未返回视图前调用此方法

 * 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示

 */

@Override

Public void postHandle(HttpServletRequest request,

HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

// TODO Auto-generated method stub

}

/**

 * controller执行后且视图返回后调用此方法

 * 这里可得到执行controller时的异常信息

 * 这里可记录操作日志,资源清理等

 */

@Override

Public void afterCompletion(HttpServletRequest request,

HttpServletResponse response, Object handler, Exception ex)

throws Exception {

// TODO Auto-generated method stub

}

 

}


3.3拦截器配置

3.3.1针对某种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"/>


3.3.2针对所有mapping配置全局拦截器

 

<!--拦截器 -->

<mvc:interceptors>

<!--多个拦截器,顺序执行 -->

<mvc:interceptor>

<mvc:mapping path="/**"/>

<bean class="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean>

</mvc:interceptor>

<mvc:interceptor>

<mvc:mapping path="/**"/>

<bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>

</mvc:interceptor>

</mvc:interceptors>


3.4正常流程测试

3.4.1代码:

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

3.4.2运行流程

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor2..postHandle..

HandlerInterceptor1..postHandle..

HandlerInterceptor2..afterCompletion..

HandlerInterceptor1..afterCompletion..

3.5中断流程测试

3.5.1代码:

定义两个拦截器分别为:HandlerInterceptor1HandlerInteptor2

3.5.2运行流程

HandlerInterceptor1preHandler方法返回falseHandlerInterceptor2返回true,运行流程如下:

HandlerInterceptor1..preHandle..

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

HandlerInterceptor1preHandler方法返回trueHandlerInterceptor2返回false,运行流程如下:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

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

总结:

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用

afterCompletion只有preHandle返回true才调用

3.6拦截器应用

3.6.1用户身份认证

Public class LoginInterceptorimplements HandlerInterceptor{

 

@Override

Public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler)throws Exception {

 

//如果是登录页面则放行

if(request.getRequestURI().indexOf("login.action")>=0){

return true;

}

HttpSession session = request.getSession();

//如果用户已登录也放行

if(session.getAttribute("user")!=null){

return true;

}

//用户没有登录挑战到登录页面

request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);

return false;

}

}

 


3.6.2用户登陆controller

//登陆页面

@RequestMapping("/login")

public String login(Model model)throws Exception{

return "login";

}

//登陆提交

//userid:用户账号,pwd:密码

@RequestMapping("/loginsubmit")

public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{

//向session记录用户身份信息

session.setAttribute("activeUser", userid);

return "redirect:item/queryItem.action";

}

//退出

@RequestMapping("/logout")

public String logout(HttpSession session)throws Exception{

//session过期

session.invalidate();

return "redirect:item/queryItem.action";

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值