SpringMVC_day02

SpringMVC_day02

1. 高级参数绑定

1.1 绑定数组

需求:接收要批量删除的商品的id。

Controller方法中可以用Integer[ ]或QueryVo的private Integer[] ids属性接收,此对象属性名要与页面name属性值相同。

controller: 
public ModelAndView delItems2(QueryVo vo) {}
jsp: <c:forEach items="${itemList }" var="item">
	<tr><td><input type="checkbox" name="ids" value="${item.id}"/></td>...</tr>
</c:forEach>
//页面中选中多个checkbox向controller方法传递ids

1.2 绑定List集合

需求:实现对商品数据的批量修改。

Controller方法中用QueryVo的private List itemList属性接收。

接收List类型的数据必须是pojo的属性,如果方法的形参为ArrayList类型无法正确接收到数据。

controller: public ModelAndView delItems2(QueryVo vo) {}
jsp的name属性必须是list属性名+下标+元素属性名
jsp:
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
	<td><input type="checkbox" name="itemList[${status.index }].id" value="${item.id }"/></td>
	<td><input type="text" name="itemList[${status.index }].name" value="${item.name }"/></td>
	...
</tr>
</c:forEach>

${current} 当前这次迭代的(集合中的)项

${status.first} 判断当前项是否为集合中的第一项,返回值为true或false

${status.last} 判断当前项是否为集合中的最

varStatus属性常用参数总结下:

${status.index} 输出行号,从0开始。

${status.count} 输出行号,从1开始。

${status.后一项,返回值为true或false

begin、end、step分别表示:起始序号,结束序号,跳跃步伐。

2. @RequestMapping

通过@RequestMapping注解可以定义不同的处理器映射规则。

2.1 URL路径映射

2.1.1 添加到方法上

@RequestMapping(value="item")或@RequestMapping("/item")/可省略
public String delItems(QueryVo vo) {}
value的值是数组,可以将多个URL映射到同一个方法。
@RequestMapping(value={"item1", "item2"})

2.1.2 添加到类上

//在class上添加@RequestMapping(url)指定通用请求前缀,限制此类下的所有方法请求url必须以请求前缀开头
@RequestMapping("/item")
public class ItemsController {}

2.2 请求方法限定

​ 除了可以对url进行设置,还可以限定请求进来的方法。

--限定GET方法
@RequestMapping(value="/updateitem.action",method=RequestMethod.GET)
如果通过POST访问则报错:
HTTP Status 405 - Request method 'POST' not supported
--限定POST方法
@RequestMapping(value="/updateitem.action",method=RequestMethod.POST)
--限定GET和POST方法
@RequestMapping(value="/updateitem.action",method = {RequestMethod.GET,RequestMethod.POST})
不写则默认支持RequestMethod的八种方法

3. Controller方法返回值

3.1 返回ModelAndView

详见SpringMVC_day01

3.2 返回void

在Controller方法形参上可以定义request和response,使用request或response指定响应结果。

--使用request转发页面
request.getRequestDispatcher("/WEB-INF/jsp/itemList.jsp").forward(request, response);
--使用response重定向页面
response.sendRedirect("/14.2SpringMVC/success.jsp");
--使用response直接显示
response.getWriter().print("hahaha");

3.3 返回字符串

3.3.1 返回逻辑视图名

controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

//指定逻辑视图名,经过视图解析器解析为jsp路径:/WEB-INF/jsp/itemList.jsp
return "itemList";

3.3.2 redirect重定向

//转发到修改此商品页面
//重定向后地址栏变为重定向的地址,行了新的request和response,所以之前的请求参数都会丢失
return "redirect:/itemEdit.action?id="+item.getId();

3.3.3 forward转发

// 转发后地址栏还是原来的请求地址,没有执行新的request和response,之前的请求参数都存在
return "forward:/itemEdit.action";

3.4 三种返回值对比

1.ModelAndView 带着数据及返回视图路径。不建议使用,因为数据和视图耦合了。
2.String 返回视图路径,model携带数据。推荐使用,解耦,数据和视图分离。
3.void ajax请求,异步请求时使用。

4. 异常处理器

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

4.1 异常处理思路

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

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

4.2 自定义异常类

​ 为了区别不同的异常,通常根据异常类型进行区分,创建一个自定义系统异常。如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

public class MyException extends Exception {
	private String msg;//存放异常信息
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public MyException(String msg) {
		super();
		this.msg = msg;
	}
}

4.3 自定义异常处理器

public class CustomHandlerException implements HandlerExceptionResolver {
	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception exception) {
		//自定义异常信息
		String msg;
		//判读异常类型
		if(exception instanceof MyException) {
			//如果是自定义异常,读取异常信息
			msg=((MyException) exception).getMsg();
			//msg=exception.getMessage();
		}else {
			//如果是运行时异常,则取错误堆栈,从堆栈中获取异常信息
			Writer out=new StringWriter();
			PrintWriter pw=new PrintWriter(out);
			exception.printStackTrace(pw);
			msg=out.toString();
		}
		ModelAndView mav=new ModelAndView();
		mav.addObject("msg", msg);
		mav.setViewName("exception");
		return mav;
	}

4.4 异常处理器配置

在springmvc.xml中添加:

<!--配置全局异常处理器-->
<bean id="customHandlerException" class="com.itheima.exception.CustomHandlerException"/>

5. 上传图片

5.1 配置图片虚拟目录

  • 在tomcat上配置

    在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:

    在浏览器访问如localhost:8080/pic/1.jpg即可访问D:developupload emp下的图片。

  • 在eclipse中配置

    双击tomcat,打开modules,. . .,复制一张图片到该文件夹,浏览器访问测试。

5.2 导入jar包

实现图片上传需要加入的jar包:

5.3 配置上传解析器

在springmvc.xml中配置文件上传解析器:

<!--配置上传解析器,id固定-->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!--设置文件上传大小限制,如为5M-->
		<property name="maxUploadSize" value="5242880"/>
	</bean>

5.4 修改jsp页面

form表单必须设置enctype属性:enctype="multipart/form-data"

5.5 实现图片上传

@RequestMapping("updateItems")
	//MultipartFile picFile用于接收上传的图片
	public String updateItems(Items item,MultipartFile picFile) throws IllegalStateException, IOException {
		//设置图片名称,不能重复
		String picName=UUID.randomUUID().toString();
		
		//获取上传的图片名称
		String originalName=picFile.getOriginalFilename();
		//获取上传的图片后缀
	//String suffix=originalName.substring(originalName.lastIndexOf("."));//.jpg
		String suffix=FilenameUtils.getExtension(originalName);//jpg
		
		//保存图片
		picFile.transferTo(new File("D:\JavaEE\04.Tomcat\img\"+picName+"."+suffix));
		//设置图片名到商品中
		item.setPic(picName+"."+suffix);
		//更新商品
		this.itemsService.updateById(item);
		//转发到修改此商品页面
		return "redirect:/itemEdit.action?id="+item.getId();
	}

6. json数据交互

6.1 @RequestBody, @ResponseBody

/*
@RequestBody注解接收http请求的json数据,将json数据转换为java对象进行绑定。
@ResponseBody注解用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
*/
	@RequestMapping("testJson")
	public @ResponseBody Items testJson(@RequestBody Items item) {
		//@ResponseBody设置将返回的数据转为json格式
		//@RequestBody将传入的json数据封装到Items对象上
		return item;
	}

6.2 SpringMVC加入json支持

导入jar包:

6.3 配置json转换器

在springmvc.xml中

--method1:使用注解驱动
<mvc:annotation-driven />
--method2:不适用注解驱动
<!--处理器适配器 -->
	<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>

7. RESTful

7.1 什么是RESTful

一种资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。

传统方式操作资源
http://127.0.0.1/item/queryItem.action?id=1	查询,GET
http://127.0.0.1/item/saveItem.action			添加,POST
http://127.0.0.1/item/updateItem.action		修改,POST
http://127.0.0.1/item/deleteItem.action?id=1	删除,GET或POST

使用RESTful操作资源
http://127.0.0.1/item/1		查询,GET
http://127.0.0.1/item		添加,POST
http://127.0.0.1/item		修改,PUT
http://127.0.0.1/item/1		删除,DELETE
传统方式每次请求的地址都在做描述,如查询时是queryItem,添加时是saveItem,修改时是updateItem,删除时是deleteItem。为了不做描述,所以使用RESTful风格。

7.2 RESTful演示

使用RESTful风格开发的接口,根据id查询商品,接口地址是:localhost:8080/item/1,从url上获取商品id,步骤如下:

  1. 使用注解@RequestMapping("item/{id}")声明请求的url;{xxx}叫做占位符

  2. 使用(@PathVariable() Integer id)获取url上的数据

@RequestMapping("/itemEdit/{id}.action")
public ModelAndView queryById(@PathVariable Integer id) {}
/*
如果@RequestMapping中表示为"item/{id}",id和形参名称一致,@PathVariable不用指定名称。如果不一致,例如"item/{ItemId}"则需要指定名称@PathVariable("itemId")
*/
//浏览器访问localhost:8080/14.2SpringMVC/itemEdit/1.action

注意两个区别

  1. @PathVariable是获取url上数据的。@RequestParam获取请求参数的(包括post表单提交)

  2. 如果加上@ResponseBody注解,就不会走视图解析器,不会返回页面,目前返回的json数据。如果不加,就走视图解析器,返回页面。

8. 拦截器

8.1 定义

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

8.2 自定义拦截器类

public class HandlerInterceptor1 implements HandlerInterceptor {
	 //controller执行前调用,return true表示继续执行,false终止执行,
	 //这里可以加入登录校验,权限拦截等。
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		return true;
	}
	//controller执行后但视图未返回前调用,
	//这里可以对模型数据进行加工处理,如加入公用信息以便页面显示。
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		System.out.println("1_postHandle");
	}
	//controller执行且视图返回后调用,
	//这里可得到执行controller时的异常信息,也可记录操作日志
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("1_afterCompletion");
	}
}

8.3 拦截器配置

在springmvc中配置拦截器:

<mvc:interceptors>
		<!--配置拦截器1-->
		<mvc:interceptor>
			<!--设置所有请求都进入拦截器
			"/*" 是拦截所有的文件夹,不包含子文件夹
			"/**" 是拦截所有的文件夹及里面的子文件夹-->
			<mvc:mapping path="/**"/>
		<bean class="com.itheima.handlerInterceptor.HandlerInterceptor1"></bean>
		</mvc:interceptor>
		<!--配置拦截器2-->
		<mvc:interceptor>
		<mvc:mapping path="/**"/>
		<bean class="com.itheima.handlerInterceptor.HandlerInterceptor2"></bean>
		</mvc:interceptor>
	</mvc:interceptors>

8.4 测试

浏览器访问localhost:8080/14.2SpringMVC/itemList.action

HandlerInterceptor1的preHandler方法返回false,HandlerInterceptor2返回true
运行结果如下:
1_preHandle
从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且Controller也不执行了。
HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false
运行结果如下:
1_preHandle
2_preHandle
1_afterCompletion
从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且controller也不执行了。
HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回true
运行结果如下:
1_preHandle
2_preHandle
2_postHandle
1_postHandle
2_afterCompletion
1_afterCompletion

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

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

拦截器定义顺序取决于springmvc配置的顺序

9. 拦截器应用

需求: 拦截用户请求,判断用户是否登录(登录请求不能拦截),如果用户已经登录,放行;如果用户未登录,跳转到登录页面。

public class HandlerInterceptor1 implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		//判断用户是否登录,如果没有登录,重定向到登录页面,不放行;如果登录,放行
		//url:http://localhost:8080/14.2SpringMVC/login.action
		//uri:/login.action
		//注意:不拦截登录请求(即uri中包含"login"的)
		String uri=request.getRequestURI();
		if(!uri.contains("login")) {
			//获取用户名
			String username=(String)request.getSession().getAttribute("username");
			//判断用户名是否为空
			if(username==null) {
				//不放行,跳转到登录界面
response.sendRedirect(request.getContextPath()+"/toLogin.action");
				return false;
			}
		}
		return true;
	}
	...
}
原文地址:https://www.cnblogs.com/ALiWang/p/12482718.html