目录
springMVC参数绑定
默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值:
HttpServletRequest
通过request对象获取请求信息
HttpServletResponse
通过response处理响应信息
HttpSession
通过session对象得到session中存放的对象
Model和ModelMap
除了ModelAndView以外,还可以使用Model来向页面传递数据,Model是一个接口,在参数里直接声明model即可。
如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代。
不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据。
代码实现:
@RequestMapping("/itemEdit") public String queryItemById(HttpServletRequest request, Model model) {//添加Model接口参数 // 从request中获取请求参数 String strId = request.getParameter("id"); Integer id = Integer.valueOf(strId); // 根据id查询商品数据 Item item = this.itemService.queryItemById(id); // 把结果传递给页面 // ModelAndView modelAndView = new ModelAndView(); // 把商品数据放在模型中 // modelAndView.addObject("item", item); // 设置逻辑视图 // modelAndView.setViewName("itemEdit"); // 把商品数据放在模型中 model.addAttribute("item", item); return "itemEdit"; }
Model:
ModelMap是Model接口的实现类,也可以通过ModelMap向页面传递数据
@RequestMapping("/itemEdit") public String queryItemById(HttpServletRequest request, ModelMap model){//ModelMap实现类参数 ......//同上 // ModelMap对象 model.addAttribute("item", item); return "itemEdit"; }
绑定简单类型:
要求:当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。
这样,从Request取参数的方法就可以进一步简化;
@RequestMapping("/itemEdit") public String queryItemById(int id, ModelMap model) {//处理器形参 // 根据id查询商品数据 Item item = this.itemService.queryItemById(id); // 把商品数据放在模型中 model.addAttribute("item", item); return "itemEdit"; }
相当于:写入形参的同时已经从表单中获取到了参数,所以可以直接调用;
支持的参数类型:
- 整形:Integer、int
- 字符串:String
- 单精度:Float、float
- 双精度:Double、double
- 布尔型:Boolean、boolean
参数类型推荐使用包装数据类型,因为基础数据类型不可以为null
说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0
@RequestParam
用途:当前端页面的请求参数名和形参不同的时候,可以使用此注解声明一下;
value:参数名字,即入参的请求参数名字,如value=“itemId”表示请求的参数 区中的名字为itemId的参数的值将传入
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
TTP Status 400 - Required Integer parameter 'XXXX' is not present
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
@RequestMapping("/itemEdit") public String queryItemById(@RequestParam(value = "itemId", required = true, defaultValue = "1") Integer id,//@RequestParam ModelMap modelMap) { //根据id查询商品数据 Item item = this.itemService.queryItemById(id); // 把商品数据放在模型中 modelMap.addAttribute("item", item); return "itemEdit"; }
绑定pojo类型
如果提交的参数很多,或者提交的表单中的内容很多的时候,可以使用简单类型接受数据,也可以使用pojo接收数据。
要求:
- pojo对象中的属性名和表单中input的name属性一致。
可以理解为
创建一个pojo的对象,将表单中的属性对应pojo的属性封装进pojo中;保存到request中;
@RequestMapping("/updateItem") public String updateItem(Item item) {//请求参数对应pojo名称 // 调用服务更新商品 this.itemService.updateItemById(item); // 返回逻辑视图 return "success"; }
绑定包装的pojo
public class QueryVo {//包装类 private Item item;//pojo set/get... }
// 绑定包装数据类型 @RequestMapping("/queryItem") public String queryItem(QueryVo queryVo) {//包装类作为对象; System.out.println(queryVo.getItem().getId()); System.out.println(queryVo.getItem().getName()); return "success"; }
高级参数绑定
绑定数组
需求:
在商品列表页面选中多个商品,然后删除。
需求分析:
功能要求商品列表页面中的每个商品前有一个checkbok类型的,选中多个商品后点击删除按钮把商品id传递给Controller,根据商品id删除商品信息。
Controller方法中可以用String[]接收,或者pojo的String[]属性接收。两种方式任选其一即可。
//包装类型 绑定数组类型,可以使用两种方式,pojo的属性接收,和直接接收 @RequestMapping("queryItem") public String queryItem(QueryVo queryVo, Integer[] ids) { System.out.println(queryVo.getItem().getId()); System.out.println(queryVo.getItem().getName()); System.out.println(queryVo.getIds().length); System.out.println(ids.length); return "success"; }
将数据绑定到list
需求
实现商品数据的批量修改
开发分析
- 在商品列表页面中可以对商品信息进行修改。
- 可以批量提交修改后的商品数据。
使用包装pojo对象进行绑定,对象内部定义一个list
public class QueryVo{ private Item item; private Integer[] ids; //使用对象的属性接收List集合 private List<Item> itemList; ... }
jsp页面:
分析发现:name属性必须是:list属性名+下标+元素属性;
你可以理解为:
在形参中发现需要itemList的属性,到表单中查找到itemList对应的表单属性;找到之后封装item对象到List集合,然后保存到域对象中;
后台需要定义一个装Item对象的List集合;将每个Item封装进List对象中;
<c:forEach items="${itemList }" var="item" varStatus="s"> <tr> <td><input type="checkbox" name="ids" value="${item.id}"/></td> <td> <!--${s.index} 输出行号,从0开始 --> <input type="hidden" name="itemList[${s.index}].id" value="${item.id }"/> <input type="text" name="itemList[${s.index}].name" value="${item.name }"/> </td> <td><input type="text" name="itemList[${s.index}].price" value="${item.price }"/></td> <td><input type="text" name="itemList[${s.index}].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td> <td><input type="text" name="itemList[${s.index}].detail" value="${item.detail }"/></td> <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td> </tr> </c:forEach>
post乱码解决
在web.xml中加入过滤器:
<!-- 解决post乱码问题 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- 设置编码是UTF8 --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
自定义处理器适配器
需求与分析
需求:可以修改条目生产日期,并且根据业务需求自定义日期格式。
分析:由于日期数据有很多种格式,springMVC没办法把字符串转换成日期类型。所以需要自定义参数绑定。
前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定。可以在springmvc处理器适配器上自定义转换器Converter进行参数绑定。
实现
自定义Converter
//Converter<S, T> //S:source,需要转换的源的类型 //T:target,需要转换的目标类型 public class DateConverter implements Converter<String, Date> { @Override public Date convert(String source) { try { // 把字符串转换为日期类型 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss"); Date date = simpleDateFormat.parse(source); return date; } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 如果转换异常则返回空 return null; } }
一般使用<mvc:annotation-driven/>
注解驱动加载处理器适配器,可以在此标签上进行配置:
<!-- 配置注解驱动 --> <mvc:annotation-driven conversion-service="conversionService" /> <!-- 转换器配置 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="自定义Converter的全限定类名" /> </set> </property> </bean>
@RequestMapping
url
@RequestMapping(value="item")或@RequestMapping("/item")
如果value的值是数组,可以将多个url映射到同一个方法
添加在类上面
在类上添加@RequestMapping(url)指定请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头
请求方法限定
//限定POST方法 @RequestMapping(method = RequestMethod.POST)
//限定GET方法 @RequestMapping(method = RequestMethod.GET)
//GET和POST都可以 @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})
Controller方法返回值
返回ModelAndView
controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。
返回void
在Controller方法形参上可以定义request和response,使用request或response指定响应结果;
有三种方式:
@RequestMapping("queryItem") public void queryItem(HttpServletRequest request, HttpServletResponse response) throws Exception { // 1 使用request进行转发 // request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request, // response); // 2 使用response进行重定向到编辑页面 // response.sendRedirect("/springmvc-web2/itemEdit.action"); // 3 使用response直接显示,例如响应json数据如下 response.getWriter().print("{\"abc\":123}"); }
返回字符串
逻辑视图名
//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/itemList.jsp return "itemList";
Redirect重定向
@RequestMapping("updateItem") public String updateItemById(Item item) { // 更新商品 this.itemService.updateItemById(item); // 修改商品成功后,重定向到商品编辑页面 // 重定向后浏览器地址栏变更为重定向的地址 // 重定向相当于执行了新的request和response,所以之前的请求参数都会丢失 // 如果要指定请求参数,需要在重定向的url后面添加 ?itemId=1 这样的请求参数 return "redirect:/itemEdit.action?itemId=" + item.getId(); }
forward转发
Controller方法执行后继续执行另一个Controller方法
由于是同一个请求,请求的参数到另一个controller中还可以使用。
@RequestMapping("updateItem") public String updateItemById(Item item) { // 更新商品 this.itemService.updateItemById(item); // 修改商品成功后,继续执行另一个方法 // 使用转发的方式实现。转发后浏览器地址栏还是原来的请求地址, // 转发并没有执行新的request和response,所以之前的请求参数都存在 return "forward:/itemEdit.action"; } //结果转发到editItem.action,request可以带过去 return "forward: /itemEdit.action";
异常处理思路
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理;
public class CustomHandleException implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) { // 定义异常信息 String msg; // 判断异常类型,,MyException为自定义的异常类 if (exception instanceof MyException) { // 如果是自定义异常,读取异常信息 msg = exception.getMessage(); } else { // 如果是运行时异常,则取错误堆栈,从堆栈中获取异常信息 Writer out = new StringWriter(); PrintWriter s = new PrintWriter(out); exception.printStackTrace(s); msg = out.toString(); } // 把错误信息发给相关人员,邮件,短信等方式 // TODO // 返回错误页面,给用户友好页面显示错误信息 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", msg); modelAndView.setViewName("error"); return modelAndView; } }
图片文件上传
加入jar包
加入fileUpload和io两个包,maven就添加依赖即可;
配置上传解析器
<!-- 文件上传,id必须设置为multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置文件上传大小 --> <property name="maxUploadSize" value="5000000" /> </bean>
jsp页面修改
<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>
<!--设置表单可以上传文件--> <form id="itemForm" action="" method="post" enctype="multipart/form-data"> <form>
图片上传
/** * 更新商品 * * @param item * @return * @throws Exception */ @RequestMapping("updateItem") public String updateItemById(Item item, MultipartFile pictureFile) throws Exception { // 图片上传 // 设置图片名称,不能重复,可以使用uuid String picName = UUID.randomUUID().toString(); // 获取文件名 String oriName = pictureFile.getOriginalFilename(); // 获取图片后缀 String extName = oriName.substring(oriName.lastIndexOf(".")); // 开始上传 pictureFile.transferTo(new File("C:/upload/image/" + picName + extName)); // 设置图片名到商品中 item.setPic(picName + extName); // --------------------------------------------- // 更新商品 this.itemService.updateItemById(item); return "forward:/itemEdit.action"; }
json
@RequestBody注解
用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容(json数据)转换为java对象并绑定到Controller方法的参数上。
@ResponseBody注解
用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端
应用
加入jar
如果需要springMVC支持json,必须加入json的处理jar:Jackson.jar,maven可以加入依赖;
Controller编写
//json数据交互 @RequestMapping(value = "/json.action") public @ResponseBody Items json(@RequestBody Items items){ // System.out.println(items); return items; }
拦截器
Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
拦截器定义
public class HandlerInterceptor1 implements HandlerInterceptor { // controller执行后且视图返回后调用此方法 // 这里可得到执行controller时的异常信息 // 这里可记录操作日志 @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)throws Exception { System.out.println("HandlerInterceptor1....afterCompletion"); } // controller执行后但未返回视图前调用此方法 // 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示 @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("HandlerInterceptor1....postHandle"); } // Controller执行前调用此方法 // 返回true表示继续执行,返回false中止执行 // 这里可以加入登录校验、权限拦截等 @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("HandlerInterceptor1....preHandle"); // 设置为true,测试使用 return true; } }
配置拦截器
<!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 所有的请求都进入拦截器 --> <mvc:mapping path="/**" /> <!-- 配置具体的拦截器 --> <bean class="com.abc.ssm.interceptor.HandlerInterceptor1" /> </mvc:interceptor> </mvc:interceptors>
应用
登录页面已有,需要写一个Controller访问登录页面
登录页面有一提交表单的动作。需要在Controller中处理
- 判断用户名密码是否正确(在控制台打印)
- 如果正确,向session中写入用户信息(写入用户名username)
- 跳转到商品列表
拦截器:
- 拦截用户请求,判断用户是否登录(登录请求不能拦截)
- 如果用户已经登录。放行
- 如果用户未登录,跳转到登录页面。
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>Insert title here</title> </head> <body> <form action="${pageContext.request.contextPath }/user/login.action"> <label>用户名:</label> <br> <input type="text" name="username"> <br> <label>密码:</label> <br> <input type="password" name="password"> <br> <input type="submit"> </form> </body> </html>
用户登录Controller:
@Controller @RequestMapping("user") public class UserController { /** * 跳转到登录页面 * * @return */ @RequestMapping("toLogin") public String toLogin() { return "login"; } /** * 用户登录 * * @param username * @param password * @param session * @return */ @RequestMapping("login") public String login(String username, String password, HttpSession session) { // 校验用户登录 System.out.println(username); System.out.println(password); // 把用户名放到session中 session.setAttribute("username", username); return "redirect:/item/itemList.action"; } }
拦截器
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { // 从request中获取session HttpSession session = request.getSession(); // 从session中获取username Object username = session.getAttribute("username"); // 判断username是否为null if (username != null) { // 如果不为空则放行 return true; } else { // 如果为空则跳转到登录页面 response.sendRedirect(request.getContextPath() + "/user/toLogin.action"); } return false; }
配置拦截器:
<mvc:interceptor> <!-- 配置商品被拦截器拦截 --> <mvc:mapping path="/item/**" /> <!-- 配置具体的拦截器 --> <bean class="com.abc.ssm.interceptor.LoginHandlerInterceptor" /> </mvc:interceptor>