SpringMVC高级参数绑定
自定义参数绑定
自定义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>
批量简单类型绑定
批量pojo类型绑定
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>
varStatus属性常用参数总结下:
${status.index} 输出行号,从0开始。
${status.count} 输出行号,从1开始。
${status.current} 当前这次迭代的(集合中的)项
${status.first} 判断当前项是否为集合中的第一项,返回值为true或false
${status.last} 判断当前项是否为集合中的最后一项,返回值为true或false
begin、end、step分别表示:起始序号,结束序号,跳跃步伐。
如果页面传入的是简单类型的批量数据,那么必须使用数组接收,这个数组的参数可以直接在controller方法形参中定义,也可以封装到一个pojo中接收。
如果页面传入的是pojo类型的批量数据,那么可以使用数组或者集合来接收,但是必须将数组或者集合声明在一个pojo中才能接收页面参数。
@RequestMapping
url路径映射
窄化请求映射
在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。
如下:
@RequestMapping放在类名上边,设置请求前缀
@Controller
@RequestMapping("/item")
方法名上边设置请求映射url:
@RequestMapping放在方法名上边,如下:
@RequestMapping("/queryItem ")
访问地址为:/item/queryItem.do
请求方法限定
@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
3、也可以通过response指定响应结果,例如响应json数据如下:
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
返回字符串(推荐)
<!-- 视图解析器 -->
<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重定向
redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/item/queryitem.action后边加参数,如下:
/item/queryitem?...&…..
forward转发
forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
异常处理器
自定义异常类
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>
异常处理器配置
@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类型解析器,文件上传 -->
<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虚拟目录
json数据交互
@ResponseBody
@RequestBody
Springmvc默认使用MappingJacksonHttpMessageConverter对json数据进行转换,所以需要加入Jackson的jar包:
<%@ 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、post、get来操作资源。分别对应添加、删除、修改、查询。不过一般使用时还是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
在springmvc.xml文件中,使用mvc:resources标签,具体如下:
<!-- 当DispatcherServlet配置为/来拦截请求的时候,需要配置静态资源的访问映射 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
Springmvc会把mapping映射到ResourceHttpRequestHandler,这样静态资源在经过DispatcherServlet转发时就可以找到对应的Handler了。
拦截器
定义拦截器
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>
配置到最前面的拦截器,优先级最高。
<%@ 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";
}