Springmvc笔记(二)

SpringMVC高级参数绑定

自定义参数绑定

页面传递的日期类型不固定,我们应该根据业务自定义日期格式

但是springmvc没办法把数字串转成统一的日期类型,所以我们需要自定义。

前端控制器接收到请求之后,找到处理器适配器,适配器执行处理器时,对方法中的形参进行参数绑定,所以如果自定义参数绑定的话,需要将该参数组件绑定到处理器适配器上,所以我们在<mvc:annotation-driven/>上进行扩展。

自定义converter
public class DateConverter implements Converter<String, Date> {

	@Override
	public Date convert(String source) {
	 
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			return simpleDateFormat.parse(source);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	<mvc:annotation-driven conversion-service="conversionService"/>
	<!-- 转换器配置 -->
	<bean id ="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<property name="converters">
	<set>
	<bean class="com.itheima.controller.converter.DateConverter"></bean>
	</set>
	</property>
	</bean>

批量简单类型绑定
比如前端的批量删除操作,需要将多个id传到后台,那么我们可以在形参使用String[]或者pojo的String[]接收,但是不能用集合接收

批量pojo类型绑定
需求:批量编辑页面数据
因为是批量接收pojo类型的数据,那么我们就要使用list集合来接收,springmvc支持包装pojo的list形式,所以我们使用包装pojo类型

定义itemQueryVO
public class itemQueryVO{
private List<Item> itemList;

}

  @RequestMapping("updateAll")
    public String updateAll(ItemQueryVO vo){
    	
		return null;
    	
    	
    }
<%@ 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 deleteAll() {
		document.itemForm.action = "${pageContext.request.contextPath }/deleteAll.do";
		document.itemForm.submit();
	}
	function updateAll() {
		document.itemForm.action = "${pageContext.request.contextPath }/user/updateAll.do";
		document.itemForm.submit();
	}
</script>
</head>
<body>
	欢迎你,${username }
	<a href="${pageContext.request.contextPath }/logout">请滚吧</a>
	
	<form name="itemForm"
		action="${pageContext.request.contextPath }/item/queryItem.do"
		method="post">
		查询条件:
		<table width="100%" border=1>
			<tr>
				<td>商品名称:<input type="text" name="item.name" /></td>
				<!-- <td>用户名称:<input type="text" name="user.name" /></td> -->
				<td><input type="submit" value="查询" /></td>
				<td><input type="button" value="批量删除" οnclick="deleteAll()" /></td>
				<td><input type="button" value="批量修改" οnclick="updateAll()" /></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="${itemList }" var="item" varStatus="status">
				<tr>
					<td><input type="checkbox" name="itemId" value="${item.id }" />
						<input type="hidden" name="itemList[${status.index }].id"
						value="${item.id }" /></td>
					<td><input type="text" name="itemList[${status.index }].name"
						value="${item.name }"></td>
					<td><input type="text" name="itemList[${status.index }].price"
						value="${item.price }"></td>
					<td><input type="text"
						name="itemList[${status.index }].createtime"
						value="<fmt:formatDate value="${item.createtime}"
							pattern="yyyy-MM-dd HH:mm:ss" />"></td>
					<td><input type="text"
						name="itemList[${status.index }].detail" value="${item.detail }"></td>

					<td><a
						href="${pageContext.request.contextPath }/item/showItemEdit.do?id=${item.id}">修改</a></td>

				</tr>
			</c:forEach>

		</table>
	</form>
</body>

</html>

主要是前端的foreach循环,用itemList[角标].属性 取值。

varStatus属性常用参数总结下:

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

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

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

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

${status.last}  判断当前项是否为集合中的最后一项,返回值为truefalse

beginendstep分别表示:起始序号,结束序号,跳跃步伐。

 


如果页面传入的是简单类型的批量数据,那么必须使用数组接收,这个数组的参数可以直接在controller方法形参中定义,也可以封装到一个pojo中接收。


如果页面传入的是pojo类型的批量数据,那么可以使用数组或者集合来接收,但是必须将数组或者集合声明在一个pojo中才能接收页面参数。

@RequestMapping

通过使用该注解可以定义不同的处理器映射规则,

url路径映射

@RequestMapping(value="/item")或者 @RequestMapping("/item")
value的值是数组,可以映射多个url到同一个方法中。
@RequestMapping(value={"/item","/query"})

窄化请求映射

在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。

如下:
@RequestMapping放在类名上边,设置请求前缀

@Controller
@RequestMapping("/item")

方法名上边设置请求映射url:
@RequestMapping放在方法名上边,如下:
@RequestMapping("/queryItem ")

访问地址为:/item/queryItem.do

请求方法限定

例如:
限定GET
@RequestMapping(value="/editItem",method=RequestMethod.GET)
限定POST方法

@RequestMapping(method = RequestMethod.POST)

如果通过Post访问则报错:
HTTP Status 405 - Request method 'GET' not supported

GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

Controller方法返回值

返回ModelAndView

例如:
@RequestMapping("list")
	public ModelAndView findUser(@RequestParam(value="uid",defaultValue="2",required=true) int id  ){
    	ModelAndView modelAndView =new ModelAndView();
    	List<User> findAllUser = service.findAllUser();
    	String username="";
    	for (User user : findAllUser) {
			username=user.getUsername();
		}
    	modelAndView.addObject("userlist", username);
    	modelAndView.setViewName("user");
    	System.out.println(id+"ddddddddddd");
	return modelAndView;
		
	}


返回 void

在controller的形参默认的有HttpRequest HttpResponse  所以用这两个来相应数据,

1.使用request转向页面
request.getRequestDispatcher("页面路径").forward(request,response);

2.response重定向页面
response.sendRedirect("url")

3、也可以通过response指定响应结果,例如响应json数据如下:

response.setCharacterEncoding("utf-8");

response.setContentType("application/json;charset=utf-8");

response.getWriter().write("json串");


返回字符串(推荐)

Controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析
例如: return “item/item-edit”,在加上我们配置了视图解析器
<!-- 视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>

所以最终的路径为 /WEB-INF/jsp/item/item-edit.jsp

redirect重定向
return "redirect:queryItem.do";

redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。

由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/item/queryitem.action后边加参数,如下:

/item/queryitem?...&…..


forward转发
return “forward:queryItem.do”

forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。


异常处理器

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

思路:
系统中的异常包括两类,编译时异常和运行时异常,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发,测试减少运行时异常的发生
系统的dao service controller出现都通过throws exception 向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图

全局只有一个异常处理器

自定义异常类
为了区分不同的异常,我们通常根据异常的类型自定义异常类,这里我们创建一个自定义系统异常
public class MyException extends Exception {
private String message;
	
	public MyException(String message) {
		super(message);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

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

}
系统遇到异常,在程序中手动抛出,前端控制器会调用全局异常处理器,
全局异常处理器:
解析异常类型,

如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示,如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)

public class MyExceptionResolver implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
			Exception arg3) {
		MyException ex =null;
		if(arg3 instanceof MyException){
			ex=(MyException) arg3;
		}else{
			ex =new MyException("未知错误");
		}
		ModelAndView modelAndView =new ModelAndView();
		modelAndView.addObject("message",ex.getMessage());
		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

<bean class="com.itheima.exception.MyExceptionResolver"></bean>

测试:
@RequestMapping(value = "/showItemEdit")
public String showItemEdit(Integer id,Model model) throws Exception{

	// 查询要显示的商品内容
	Item item = itemService.queryItemById(id);

	if(item == null) throw new MyException("查询不到商品无法修改");

	return "item/item-edit";
}

上传文件

Multipart解析器需要依赖一下文件上传的jar包

在页面的form中提交的enctype=multipart/form-data 的数据时,我们需要进行解析,所以加入一个解析器

springmvc.xml中配置 multipart类型解析器
<!-- multipart类型解析器,文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!-- 上传文件的最大尺寸 5M-->
	<property name="maxUploadSize" value="5242480"/>
</bean>


   @RequestMapping("editItem")
    public String editItem(Item item,
			MultipartFile pictureFile) throws Exception, IOException{
    	// 处理上传文件
    	
    			try {
					if (pictureFile != null) {
						// 获取上传文件格式

						// 上传文件的原始名称
						String originalFilename = pictureFile.getOriginalFilename();

						String extName = "";
						if (originalFilename != null && !"".equals(originalFilename)) {
							// 得到扩展名,比如: .jpg
							extName = originalFilename.substring(originalFilename.lastIndexOf("."));
						}

						// 创建新的文件名
						String newFileName = UUID.randomUUID().toString() + extName;

						// 指定上传文件存储目录
						String dir = "E:\\img\\";

						// 保证存储目录一定存在
						File dirFile = new File(dir);
						if (!dirFile.exists()) {
							dirFile.mkdirs();
						}

						// 将上传文件保存到新文件中
						pictureFile.transferTo(new File(dir + newFileName));

						// 保存新文件名
						item.setPic(newFileName);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

    			// 修改商品
    			service.updateItem(item);

    			// forward
    			// return "forward:/item/queryItem.do";

    			// redirect
    			return "redirect:/user/itemList.do";
    	
    	
    }

<update id="updateItem" parameterType="item" >
update item set name=#{name},pic=#{pic},price=#{price}  where id =#{id}
</update>

配置tomcat虚拟目录


我们选择 e:\\img

json数据交互

两个流程:
请求是key-value数据,响应json
请求是json,响应是json
我们需要用到@ResponseBody和@RequestBody

@ResponseBody
responseBody注解可以通过内置的9种HttpMessageConverter,匹配不同的Controller 返回值类型,然后进行不同的消息转换处理,然后将转换之后的数据放到HttpServletResponse对象的响应体返回到页面
不同的HttpMessageConverter处理的数据,指的是ContentTyoe值也不同。
@RequestBody
与@ResponseBody注解相反,它是处理请求参数的,演示 MappingJacksonHttpMessageConverter,专门处理pojo类型返回值

Springmvc默认使用MappingJacksonHttpMessageConverterjson数据进行转换,所以需要加入Jacksonjar包:



springmvc.xml中 <mvc:annotation-drivern>标签默认配置了json转换器
<%@ 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">
	function responseKV() {
		$.ajax({
			type : "post",
			url : '${pageContext.request.contextPath }/requestKV.do',
			//输入是key/value时,默认就指定好了contentType了,不需要再指定了
			//contentType:'application/json;charset=utf-8',
			//data为key/value形式
			data : 'name=json测试&price=999',
			success : function(data) {
				alert(data.name);
			}
		});
	}

	function requestJSON() {
		$.ajax({
			type : "post",
			url : '${pageContext.request.contextPath }/requestJSON.do',
			//输入是json是 ,需要指定contentType为application/json
			contentType : 'application/json;charset=utf-8',
			data : '{"name":"json测试","price":999}',
			success : function(data) {
				alert(data.name);
			}
		});
	}
</script>
</head>
<body>
	<input type="button" value="请求KV" οnclick="requestKV();" />
	<input type="button" value="请求JSON" οnclick="requestJSON();" />
</body>
</html>
@Controller
public class JsonController {

// 输入是json,输出是json

       // @RequestBody 将请求的json串转成java对象

       // @ResponseBody 将返回值转成json串响应给前台

@RequestMapping("/requestKV") @ResponseBody public Item requestKV(Item item){ return item; } @RequestMapping("/requestJSON") @ResponseBody public Item requestJSON(@RequestBody Item item){ return item; }


Restful风格支持

Restful是一种web软件架构风格,它不是标准也不是协议,它倡导的是一个资源定位及资源操作的风格。

 

URL:资源定位符,通过URL地址去定位互联网中的资源(抽象的概念,比如图片、视频、app服务等)。

 

RESTful风格URL:互联网所有的事物都是资源,要求URL中只有表示资源的名称,没有动词。比如:http://write.blog.csdn.net/LawsonJin/a/666 

RESTful风格资源操作:使用HTTP请求中的method方法put、delete、postget来操作资源。分别对应添加、删除、修改、查询。不过一般使用时还是post和get。Put和Delete几乎不使用。

 

RESTful风格资源表述:可以根据需求对URL定位的资源返回不同的表述(也就是返回数据类型,比如XML、JSON等数据格式)。

 

传统URL风格:

http://localhost:8080/ssm/queryItem.do?name=zhangsan&age=999

 

RESTful URL风格:

http://localhost:8080/ssm/item/zhangsan/999

http的method会告知到底对item资源做什么操作。

 

springmvc支持RESTful,具体讲的就是使用@PathVariable注解获取restful风格的请求URL中的路径变量。

添加DispatcherServlet的restful配置

就是把拦截改成 /

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


    @RequestMapping(value="/it/{id}",method=RequestMethod.GET)
	@ResponseBody
	public Item getItem(@PathVariable("id") Integer id){
		Item item = service.finItemById(id);
		return item;
		
	}
http://localhost:8080/sss/user/it/1

这会向服务端发送get请求,然后直接定位到/it/1
@PathVariable可以取值
但是我们配置这样的话,静态资源是找不到匹配的适配器的,所以我们要对静态资源进行访问处理,
method可以指定接收什么请求类型,非get请求不接收

springmvc.xml文件中,使用mvc:resources标签,具体如下:

<!-- DispatcherServlet配置为/来拦截请求的时候,需要配置静态资源的访问映射 -->

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

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

Springmvc会把mapping映射到ResourceHttpRequestHandler,这样静态资源在经过DispatcherServlet转发时就可以找到对应的Handler了。


mapping:相对于工程来讲是/js下面的** 代表子目录子子目录,第一个/相当于 localhost:8280/ssm
location: 指的是目录,第一个/相当于 WebContext


拦截器

定义拦截器

实现 HandlerIntercepter
public class MyHandlerInterceptor implements HandlerInterceptor {
	//执行完Handler之后调用
	//应用场景:统一异常处理、统一日志处理

	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		// TODO Auto-generated method stub

	}
	//进入Handler开始执行,并且在返回ModelAndView之前调用
		//应用场景:对ModelAndView对象操作,可以把公共模型数据传到前台,可以统一指定视图

	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		// TODO Auto-generated method stub

	}
	
	//Handler执行前调用
		//应用场景:登录认证、身份授权
		//返回值为true则是放行,为false是不放行

	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

配置拦截器

Springmvc拦截器针对HandlerMapping进行拦截处理,即;如果某个HandlerMapping中配置拦截,则该HandlerMapping映射成功的Handler会使用该拦截器


全局拦截器配置:

Springmvc的全局拦截器配置,其实是把配置的拦截器注入到每个HandlerMapping中了

<mvc:interceptors>
<!-- 如果有很多拦截器,那么按顺序执行 -->
<mvc:interceptor>
<!-- path中 /** 代表所有url和子url路径 -->
<mvc:mapping path="/**"/>
</mvc:interceptor>
</mvc:interceptors>


配置到最前面的拦截器,优先级最高。

简单登录拦截器案例:

login.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>登录页面</title>
</head>
<body>
	<form action="${pageContext.request.contextPath }/login" method="post">
		<table align="center" border="1" cellspacing="0" >
			<tr>
				<td>用户名:<input type="text" name="username"/></td>
			</tr>
			<tr>
				<td>密    码:<input type="text" name="password"/></td>
			</tr>
			<tr>
				<td><input type="submit" value="登录"/></td>
			</tr>
		</table>
	</form>
</body>
</html>

logininterceptor
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		// 获取请求URL
				String uri = request.getRequestURI();
				// 判断是否公开页面(是否包含login字符串)
				if (uri.indexOf("login") > 0) {
					return true;
				}
				// 判断是否登录
				String username = (String) request.getSession().getAttribute("username");

				// 没有登录,则跳到登录页面
				if (username == null || username.equals("")) {
					// 如果不是,则跳到登录页面
					response.sendRedirect("/ssm/loginPage");
					return false;
				}

				return true;
	}
登录方法
	@RequestMapping("/login")
	public String login(String username, String password, HttpSession session) {
		if (username == null || username.equals("")) {
			return "login";
		}
		// 将username放入session中
		session.setAttribute("username", username);

		// 重定向到商品列表页面
		return "redirect:/item/queryItem";
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LawsonJin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值