Spring MVC

springmvc

springmvc是什么

springmvc是spring的一个模块,是一个基于MVC的一个框架。

springmvc的运行原理

组件:

  • 前端控制器(DispatcherServlet):用于接收请求,响应结果,相当于转发器,中央处理器。
  • 处理器映射器:根据url查找handler
  • 处理器适配器:按照特定的规则执行handler
  • 处理器(需要程序员开发):执行具体的逻辑,实现功能
  • 视图解析器:进行视图解析,根据逻辑视图名解析成真正的视图·
  • 视图:view是一个接口,实现支持不同的view类型。

执行流程

  1. 发起请求到前端控制器
  2. 前端控制器请求处理器映射器查找handler,可以根据xml配置文件和注解进行查找
  3. 处理器映射器向前端控制器返回handler
  4. 前端控制器调用处理器适配器去执行handler
  5. 处理器适配器执行handler
  6. handler向处理器适配器返回一个ModelAandView
  7. 处理器适配器将ModelAndView返回给前端控制器
  8. 前端控制器请求视图解析器解析视图,根据逻辑视图名返回真正的视图(jsp)
  9. 视图解析器向前端控制器返回view
  10. 前端控制器将视图进行渲染,渲染视图将模型数据(在ModelAndView对象中)填充到request域。
  11. 前端控制器向用户相应结果

springmvc入门程序(注解开发)

注意:当运用框架时一定要注意框架版本与jdk的兼容性问题

  1. 导包
  2. 在web.xml中配置前端控制器
<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 配置初始化参数,加载springmvc.xml配置文件 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	</servlet>
	
	<servlet-mapping>
	<!-- url有2种配置方式:
	    *.action:以.action结尾的请求会由前端控制器处理
	    /:所有的请求(包括静态资源)都会被前端控制器处理
	    /*:这种方式是错误的
	-->
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>

springmvc.xml配置文件

<!-- 首先导入spring约束 -->
<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 ">
		
	<!-- 	开启组件扫描注解,加载handler -->
	<context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan>
	
	<!-- 	开启注解开发springmvc,同时配置注解处理器映射器和注解处理器适配器 -->
	<mvc:annotation-driven></mvc:annotation-driven>
	
	<!-- 	配置视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	
    <!-- 	配置视图解析器的前缀和后缀 -->
		<property name="prefix" value="/WEB-INF/jsp/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	</beans>	
 

书写handler类

//表明此类是handler
@Controller    
    //当@RequestMapping标注在类上时该类中所有方法都将映射为相对于类级别的请求,表示该控制器所处理的所有请求都被映射到value属性所指定的路径下
	@RequestMapping(value="/Items")
public class ItemsController2{
    //配置url与handler方法的映射,method表示支持的请求方式
	@RequestMapping(value="/queryItems",method={RequestMethod.GET,RequestMethod.POST})             
	public ModelAndView itemsList()  throws Exception{
		//调用service查找 数据库,查询商品列表,这里使用静态数据模拟
		List<Items> itemsList = new ArrayList<Items>();
		//向list中填充静态数据
		
		Items items_1 = new Items();
		items_1.setName("联想笔记本");
		items_1.setPrice(6000f);
		items_1.setDetail("ThinkPad T430 联想笔记本电脑!");
		
		Items items_2 = new Items();
		items_2.setName("苹果手机");
		items_2.setPrice(5000f);
		items_2.setDetail("iphone6苹果手机!");
		
		itemsList.add(items_1);
		itemsList.add(items_2);

		//返回ModelAndView
		ModelAndView modelAndView =  new ModelAndView();
		//相当 于request的setAttribut,在jsp页面中通过itemsList取数据
		modelAndView.addObject("itemsList", itemsList);
		
		//指定视图
		modelAndView.setViewName("items/itemsList");

		return modelAndView;

	}
}
  • 对静态资源的解析
    配置前端控制器的url-parttern中指定/,对静态资源的解析出现问题:
    在springmvc.xml中添加静态资源解析方法
<!-- 静态资源解析
	包括 :js、css、img、..
	 -->
	 <mvc:resources location="/js/" mapping="/js/**"/>
	 <mvc:resources location="/img/" mapping="/img/**"/>

Controller方法常见的三种返回值

  • 返回ModelAndView:需要方法结束时返回ModelAndView对象
  • 返回String(通常使用,也建议使用):实现了数据和视图之间的解耦合
  • 返回void:主要用于异步请求,只会返回数据,不会跳转视图
    返回String类型示例
public String test(Model model){
   model.addAttribute("msg","这是我的第一个springmvc程序");
   //重定向请求路径 
   return "redirect:queryUser";
}
public String test(Model model){
   model.addAttribute("msg","这是我的第一个springmvc程序");
   //请求转发
   return "forward:queryUser";
}

数据绑定

绑定类型

  • 常用的默认参数类型绑定:HttpServletRequest、HttpServletResponse、HttpSession、Model、ModelMap
  • 简单数据类型绑定:Integer、String····
  • 绑定POJO类型
    注意:在使用POJO类型数据绑定时,前端的name参数值一定要和pojo类中的成员变量名一致,才能够实现参数的绑定
  • 包装pojo参数的绑定,前端的name参数值一定要为被包装的pojo类型.被包装的pojo类型中的成员变量名的形式、
  • 复杂参数绑定类型:数组、List、Map

简单类型参数和pojo类型绑定示例

前台页面

<form action="${pageContext.request.contextPath }/login.action" method="post">
	用户账号:<input type="text" name="username" /><br/>
	用户密码:<input type="password" name="password" /><br/>
		      <input type="submit" value="登陆"/>
</form>

pojo类

public Class User{
    private String username;
    private String password;
    public void setUsername(String username){
        username=this.username;
    }
     public void setPassword(String username){
        passsword=this.password;
    }
    public String getUsername(){
        return this.username;
    }
    public String getPassword(){
        return this.password;
    }
}

Controller层

@requestMapping("/login")
public String login(Model model,String username,String password){
}

包装类型pojo类型参数绑定

pojo

public Class User{
    private String username;
    private String password;
    public void setUsername(String username){
        username=this.username;
    }
     public void setPassword(String username){
        passsword=this.password;
    }
    public String getUsername(){
        return this.username;
    }
    public String getPassword(){
        return this.password;
    }
}

包装pojo

public calss Items{
    private User user;
    
    private String order;
    public void setUser(User user){
        this.user=user;
    }
    public User getUser{
        return user;
    }
    
     public void setOrder(String order){
        this.order=order;
    }
    public User getOrder{
        return order;
    }
}

前台页面

<input name="user.username" type="text"/>
<input name="order" type="text"/>

controller

public String queryItems(Items item){
    
}

数组类型参数绑定(一般用于批量删除)

  • 将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
    controller方法的定义
@RequestMapping("/deleteItems"){
    public String deleteItems(Integer [] items_id)throws Exception
}

页面定义

<c:forEach items="${itemsList }" var="item">
<tr>	

	<td><input type="checkbox" name="items_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>

list绑定

  • 通常在需要批量提交数据时,将提交的数据绑定到list中,比如:成绩录入(录入多门课成绩,批量提交),
    本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
  • 表现层实现
    controller方法定义:
    1、进入批量商品修改页面(页面样式参考商品列表实现)
    2、批量修改商品提交
    使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list属性
public class ItemsQueryVo{
    private Items items;
    
    private ItemsCustom itemsCustom;
    //批量商品信息
    private List<ItemsCustom> itemsList;
}

Controller方法

public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo)throws Exception{
    
}
  • 页面定义
<c:forEach items="${itemsList }" var="item" varStatus="status">
<tr>	

	<td><input name="itemsList[${status.index }].name" value="${item.name }"/></td>
	<td><input name="itemsList[${status.index }].price" value="${item.price }"/></td>
	<td><input name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
	<td><input name="itemsList[${status.index }].detail" value="${item.detail }"/></td>


</tr>
</c:forEach>

map绑定

  • 也通过在包装pojo中定义map类型属性。
    在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。

包装类中定义Map对象如下:

Public class QueryVo {
    private Map<String, Object> itemInfo = new HashMap<String, Object>();
      //get/set方法..
}

页面定义如下:

<tr>
    <td>学生信息:</td>
    <td>
        姓名:<inputtype="text"name="itemInfo['name']"/>
        年龄:<inputtype="text"name="itemInfo['price']"/>
    .. .. ..
    </td>
</tr>

Contrller方法定义如下:

public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
    System.out.println(queryVo.getStudentinfo());
}

springmvc校验需求

  • springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
  1. 校验思路:

    页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。

  2. 具体需求:

    商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。

  • 在springmvc.xml文件中添加配置
<!-- 校验器 -->
	<bean id="validator"
		class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
		<!-- hibernate校验器-->
		<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>
	
	
	<!-- 将校验器注入到适配器中 -->
	<mvc:annotation-driven conversion-service="conversionService"
	validator="validator"></mvc:annotation-driven>
  • 在pojo中添加校验规则
//校验名称在1到30字符中间
    //message是提示校验出错显示的信息
    //groups:此校验属于哪个分组,groups可以定义多个分组
    @Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class})
    private String name;

    private Float price;

    private String pic;

    //非空校验
    @NotNull(message="{items.createtime.isNUll}")
    private Date createtime;

  • 在CustomValidationMessages.properties配置校验错误信息:
items.name.length.error=请输入1-30个字符的商品名称
items.createtime.isNUll=请输入商品的生产日期
  • 捕获校验错误信息
// 商品信息修改提交
// 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
// bindingResult接收校验出错信息
// 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
// value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
// @ModelAttribute可以指定pojo回显到页面在request中的key
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(Model model,Integer id,
        @Validated ItemsCustom itemsCustom,
		BindingResult bindingResult
		) throws Exception {
	// 获取校验错误信息
	if (bindingResult.hasErrors()) {
		// 输出错误信息
		List<ObjectError> allErrors = bindingResult.getAllErrors();

		for (ObjectError objectError : allErrors) {
			// 输出错误信息
			System.out.println(objectError.getDefaultMessage());

		}
		// 将错误信息传到页面
		model.addAttribute("allErrors", allErrors);
		
		
		//可以直接使用model将提交pojo回显到页面
		model.addAttribute("items", itemsCustom);
		
		// 出错重新到商品修改页面
		return "items/editItems";
	}
  • 在页面显示错误信息
<!-- 显示错误信息 -->
<c:if test="${allErrors!=null }">
	<c:forEach items="${allErrors }" var="error">
	${ error.defaultMessage}<br/>
</c:forEach>
</c:if>

校验分组

public interface ValidGroup1 {
	//接口中不需要定义任何方法,仅是对不同的校验规则进行分组
	//此分组只校验商品名称长度

}

  • 在校验规则中添加分组
//校验名称在1到30字符中间
//message是提示校验出错显示的信息
//groups:此校验属于哪个分组,groups可以定义多个分组
@Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class})
private String name;

  • 在指定controller使用分组
@RequestMapping("/editItemsSubmit")
	public String editItemsSubmit(
		Model model,
		HttpServletRequest request,
		Integer id,
		@ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
		) throws Exception {
		
	}

数据回显

什么是数据回显

提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。

pojo数据回显方法

  1. springmvc默认对pojo数据进行回显。
    pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)
    使用@ModelAttribute指定pojo回显到页面在request中的key

  2. @ModelAttribute还可以将方法的返回值传到页面
    在商品查询列表页面,通过商品类型查询商品信息。
    在controller中定义商品类型查询方法,最终将商品类型传到页面。

// 商品分类
//itemtypes表示最终将方法返回值放在request中的key
@ModelAttribute("itemtypes")
public Map<String, String> getItemTypes() {

	Map<String, String> itemTypes = new HashMap<String, String>();
	itemTypes.put("101", "数码");
	itemTypes.put("102", "母婴");

	return itemTypes;
}
  • jsp页面中获取方法的返回值
商品类型:
<select name="itemtype">
	<c:forEach items="${itemtypes }" var="itemtype">
		<option value="${itemtype.key }">${itemtype.value }</option>		
	</c:forEach>
</select>

异常处理

异常处理思路

  • 系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
  • 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理
  • springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

自定义异常类

对不同的异常类型定义异常类,继承Exception

package cn.itcast.ssm.exception;

/**
 * <p>Title: CustomException</p>
 * <p>Description:系统 自定义异常类,针对预期的异常,需要在程序中抛出此类的异常 </p>
 */
public class CustomException extends Exception {
	
	//异常信息
	public String message;
	
	public CustomException(String message){
		super(message);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

全局异常处理器

  • 思路:
    系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。
  • 全局异常处理器处理思路:
    解析出异常类型
    如果该异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
    如果该异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)

springmvc提供一个HandlerExceptionResolver接口

public class CustomExceptionResolver implements HandlerExceptionResolver {
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		//handler就是处理器适配器要执行Handler对象(只有method)
		
//		解析出异常类型
//		如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
//		String message = null;
//		if(ex instanceof CustomException){
//			message = ((CustomException)ex).getMessage();
//		}else{
			如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
//			message="未知错误";
//		}
		
		//上边代码变为
		CustomException customException = null;
		if(ex instanceof CustomException){
			customException = (CustomException)ex;
		}else{
			customException = new CustomException("未知错误");
		}
		
		//错误信息
		String message = customException.getMessage();
		
		ModelAndView modelAndView = new ModelAndView();
		
		//将错误信息传到页面
		modelAndView.addObject("message", message);
		
		//指向错误页面
		modelAndView.setViewName("error");

		return modelAndView;
	}

}

错误页面

<%@ 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>错误提示</title>
</head>
<body>
${message }
</body>
</html>

在springmvc.xml中配置全局异常处理器

<!-- 全局异常处理器
	只要实现HandlerExceptionResolver接口就是全局异常处理器
	 -->
	<bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>

使用时抛出异常即可

public ItemsCustom findItemsById(Integer id)throws Exception{
    if(id==null){
        throw new CustomException("修改的商品信息不存在");
    }
}
  • 注意:如果与业务功能相关的异常,建议在service中抛出异常。与业务功能没有关系的异常,建议在controller中抛出。上边的功能,建议在service中抛出异常。

上传图片

springmvc中对多部件类型解析

  • 在 页面form中提交enctype="multipart/form-data"的数据时,需要springmvc对multipart类型的数据进行解析。在springmvc.xml中配置multipart类型解析器。
<!-- 文件上传 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 设置上传文件的最大尺寸为5MB -->
		<property name="maxUploadSize">
			<value>5242880</value>
		</property>
	</bean>

加入上传图片的jar

  • commons-fileupload=1.2.2.jar和commons-io-2.4.jar

创建图片虚拟目录存储图片

  • 方法一:用开发工具配置(不作讲解)
  • 方法二:也可以直接修改tomcat的配置,在conf/server.xml文件,添加虚拟目录 :
    Context docBase="虚拟目录对应的物理目录" path="虚拟目录" reloadable="false"/>
    注意:在图片虚拟目录中,一定将图片目录分级创建(提高i/o性能),一般我们采用按日期(年、月、日)进行分级创建。

上传图片代码

页面:

<tr>
	<td>商品图片</td>
	<td>
		<c:if test="${items.pic !=null}">
			<img src="/pic/${items.pic}" width=100 height=100/>
			<br/>
		</c:if>
		<input type="file"  name="items_pic"/> 
	</td>
</tr>

controller方法

	@RequestMapping("/editItemsSubmit")
	public String editItemsSubmit(
			Model model,
			HttpServletRequest request,
			Integer id,
			@ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
			BindingResult bindingResult,
			MultipartFile items_pic//接收商品图片
			) throws Exception {
		//原始名称
		String originalFilename = items_pic.getOriginalFilename();
		//上传图片
		if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
			
			//存储图片的物理路径
			String pic_path = "F:\\develop\\upload\\temp\\";
			
			
			//新的图片名称
			String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
			//新图片
			File newFile = new File(pic_path+newFileName);
			
			//将内存中的数据写入磁盘
			items_pic.transferTo(newFile);
			
			//将新图片名称写到itemsCustom中
			itemsCustom.setPic(newFileName);
			
		}
	}

json数据交互

为什么要进行json数据交互

  • json数据格式在接口调用中、html页面中较常用,json格式比较简单,解析还比较方便。
    比如:webservice接口,传输json数据.

springmvc进行json交互

两种方式

  1. 客户端请求的是json串
  • 需要指定contentType=application/json
  • @RequestBody将json串转换成java对象
  • @ResponseBody将json串转换成json串输出
  1. 客户端请求的是key-value数据
  • 不需要指定contentType,因为默认的请求格式就是key-value格式
  • 不需要用@RequestBody将json串转换成java对象
  • @ResponseBody将java对象转换成json串输出
  1. 共同点:最终都输出json数据,为了在前端页面方便对请求结果进行解析
  2. 客户端请求json串不方便,需要将请求的内容转换成json串
  3. 请求key-value,输出json,此方法比较常用

环境准备

  • 加载json转换的jar包:springmvc中使用jackson的包进行json转换(@requestBody和@responseBody使用下边的包进行json转),如下:
    jackson-core-asl-1.9.11.jar
    jackson-mapper-asl-1.9.11.jar

配置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 /> 则不用定义上边的内容。

输入json串,输出是json串

jsp页面使用jquery的ajax提交json串,对输出的json结果进行解析。

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
//请求json,输出是json
function requestJson(){
	
	$.ajax({
		type:'post',
		url:'${pageContext.request.contextPath }/requestJson.action',
		contentType:'application/json;charset=utf-8',
		//数据格式是json串,商品信息
		data:'{"name":"手机","price":999}',
		success:function(data){//返回json结果
			alert(data);
		}
		
	});
}

controller

@Controller
public class JsonTest {
	
	//请求json串(商品信息),输出json(商品信息)
	//@RequestBody将请求的商品信息的json串转成itemsCustom对象
	//@ResponseBody将itemsCustom转成json输出
	@RequestMapping("/requestJson")
	public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
		
		//@ResponseBody将itemsCustom转成json输出
		return itemsCustom;
	}

输入key/value,输出是json串

1.1.1.1 jsp页面
使用jquery的ajax提交key/value串,对输出的json结果进行解析。

//请求key/value,输出是json
function responseJson(){
	$.ajax({
		type:'post',
		url:'${pageContext.request.contextPath }/responseJson.action',
		//请求是key/value这里不需要指定contentType,因为默认就 是key/value类型
		//contentType:'application/json;charset=utf-8',
		//数据格式是json串,商品信息
		data:'name=手机&price=999',
		success:function(data){//返回json结果
			alert(data.name);
		}
		
	});
}
</script>

controller

	
	//请求key/value,输出json
	@RequestMapping("/responseJson")
	public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
		
		//@ResponseBody将itemsCustom转成json输出
		return itemsCustom;
	}

}

拦截器

拦器截定义

  • 定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。
public class HandlerInterceptor1 implements HandlerInterceptor {

	
	//进入 Handler方法之前执行
	//用于身份认证、身份授权
	//比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//return false表示拦截,不向下执行
		//return true表示放行
		return false;
	}

	//进入Handler方法之后,返回modelAndView之前执行
	//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		
		
	}

	//执行Handler完成执行此方法
	//应用场景:统一异常处理,统一日志处理
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		
		
	}

}

拦截器配置

  • 针对HandlerMapping配置(一般不推荐使用)
    springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该 拦截器。
<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"/>
  • 类似全局的拦截器
    springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中

	<!--拦截器 -->
<mvc:interceptors>
	<!--多个拦截器,顺序执行 -->
	<mvc:interceptor>
		<!-- /**表示所有url包括子url路径 -->
		<mvc:mapping path="/**"/>
		<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1"></bean>
	</mvc:interceptor>
	<mvc:interceptor>
		<mvc:mapping path="/**"/>
		<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2"></bean>
	</mvc:interceptor>
</mvc:interceptors>

拦截测试

  • 测试需求:
    测试多个拦截器各各方法执行时机。

  • 编写两个拦截器:HandlerInterceptor1和HandlerInterceptor2

  1. 两个拦截器都放行
    HandlerInterceptor1…preHandle
    HandlerInterceptor2…preHandle
    ·································································
    HandlerInterceptor2…postHandle
    HandlerInterceptor1…postHandle
    ·································································
    HandlerInterceptor2…afterCompletion
    HandlerInterceptor1…afterCompletion
  • 总结:
    preHandle方法按顺序执行,
    postHandle和afterCompletion按拦截器配置的逆向顺序执行。
  1. 拦截器1放行,拦截器2不放行
    HandlerInterceptor1…preHandle
    HandlerInterceptor2…preHandle
    HandlerInterceptor1…afterCompletion
  • 总结:
    拦截器1放行,拦截器2 preHandle才会执行。
    拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
    只要有一个拦截器不放行,postHandle不会执行。
  1. 拦截器1不放行,拦截器2不放行
    HandlerInterceptor1…preHandle
  • 总结:
    拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
    拦截器1 preHandle不放行,拦截器2不执行。

小结

  • 根据测试结果,对拦截器应用。
    比如:统一日志处理拦截器,需要该 拦截器preHandle一定要放行,且将它放在拦截器链接中第一个位置。
    比如:登陆认证拦截器,放在拦截器链接中第一个位置。权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过后才校验权限)

拦截器应用(实现登陆认证)

  • 需求
  1. 用户请求url
  2. 拦截器进行拦截校验
    如果请求的url是公开地址(无需登陆即可访问的url),让放行
    如果用户session 不存在跳转到登陆页面
    如果用户session存在放行,继续操作。
  • 登陆controller方法
@Controller
public class LoginController {

	// 登陆
	@RequestMapping("/login")
	public String login(HttpSession session, String username, String password)
			throws Exception {

		// 调用service进行用户身份验证
		// ...

		// 在session中保存用户身份信息
		session.setAttribute("username", username);
		// 重定向到商品列表页面
		return "redirect:/items/queryItems.action";
	}

	// 退出
	@RequestMapping("/logout")
	public String logout(HttpSession session) throws Exception {

		// 清除session
		session.invalidate();

		// 重定向到商品列表页面
		return "redirect:/items/queryItems.action";
	}

}
  • 登陆认证拦截实现
    代码实现
public class LoginInterceptor implements HandlerInterceptor {

	
	//进入 Handler方法之前执行
	//用于身份认证、身份授权
	//比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//获取请求的url
		String url = request.getRequestURI();
		//判断url是否是公开 地址(实际使用时将公开 地址配置配置文件中)
		//这里公开地址是登陆提交的地址
		if(url.indexOf("login.action")>=0){
			//如果进行登陆提交,放行
			return true;
		}
		
		//判断session
		HttpSession session  = request.getSession();
		//从session中取出用户身份信息
		String username = (String) session.getAttribute("username");
		
		if(username != null){
			//身份存在,放行
			return true;
		}
		
		//执行这里表示用户身份需要认证,跳转登陆页面
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
		
		//return false表示拦截,不向下执行
		//return true表示放行
		return false;
	}
  • 拦截器的配置
<mvc:interceptor>
	<mvc:mapping path="/**"/>
	<bean class="cn.itcast.ssm.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值