Springmvc中的一些问题解决
Spring mvc中的异常
做统一处理,全局异常处理器
异常处理类代码: 必须实现接口 然后返回值是ModelAndView 那就可以做个展示页面了
package com.toov5.mvc.utils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; public class CustomerExceptionResolver implements HandlerExceptionResolver{ public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mav = new ModelAndView(); mav.addObject("msg","系统发生异常,请联系管理员!"); mav.setViewName("msg"); return mav; } }
这个类要告诉 spring mvc 所以要配置下:
<!-- 全局异常处理器配置 --> <bean class="com.toov5.mvc.utils.CustomerExceptionResolver"> </bean>
访问controller 1/0的错误
所有页面都返回这个也不是很好~
升级:
更友好 更灵活的
自定义异常 :
自定义异常类:
package com.toov5.mvc.utils; public class MyException extends Exception { private String msg; public MyException(String msg) { super(); this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
Controller的使用:
@RequestMapping("queryVoid") public void queryVoid(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException, MyException { response.setCharacterEncoding("utf-8"); //假设这里是根据ID查询商品信息 搜索不到商品 if (true) { throw new MyException("搜索失败!"); } int i = 1/0; PrintWriter writer = response.getWriter(); writer.println("rsponse打印的消息。。。。"); }
访问结果:
小结: 自定异常类 实现全局异常处理器 配置异常处理器
Spring mvc中的图片上传:
配置浏览器访问的 目录 在tomcat配置虚拟目录
本地的目录 映射到配置的虚拟目录
通过: http://localhost:8080/pic/aa.jpg 可以获取到图片
上传图片需要jar包
配置多媒体解析器: 配置的id也是固定不变的
<!-- 配置多媒体处理器 --> <!-- 注意:这里id必须填写:multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 最大上传文件大小 --> <property name="maxUploadSize" value="8388608" /> </bean>
Controller:
@RequestMapping("updateItem") public String updatItem(Item item, Model model, MultipartFile pictureFile) throws IllegalStateException, IOException { //pictureFile图片名字要对应 //防止被覆盖 图片新名字 String newName = UUID.randomUUID().toString(); String oldName = pictureFile.getOriginalFilename(); //图片原来的名字。 //后缀获取 String sux = oldName.substring(oldName.lastIndexOf(".")); //新建本地文件流 File file = new java.io.File("C:\\Users\\Administrator\\Desktop\\pics\\"+newName+sux); //写入本地磁盘 pictureFile.transferTo(file); //写入本地磁盘 //保存图片到数据库 item.setPic(newName+sux); model.addAttribute("item",item); model.addAttribute("msg","修改商品信息成功!"); return "itemEdit"; }
前端:
<%@ 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> </head> <body> <span>${msg}</span> <!-- 上传图片是需要指定属性 enctype="multipart/form-data" --> <!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> --> <form id="itemForm" action="${pageContext.request.contextPath }/updateItem.action" method="post" enctype="multipart/form-data">> <input type="hidden" name="id" value="${item.id }" /> 修改商品信息: <table width="100%" border=1> <tr> <td>商品名称</td> <td><input type="text" name="name" value="${item.name }" /></td> </tr> <tr> <td>商品价格</td> <td><input type="text" name="price" value="${item.price }" /></td> </tr> <tr> <td>商品生产日期</td> <td><input type="text" name="createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td> </tr> <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> <tr> <td>商品简介</td> <td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea> </td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="提交" /> </td> </tr> </table> </form> </body> </html>
注意:
前端表格:<form id="itemForm" action="${pageContext.request.contextPath }/updateItem.action" method="post" enctype="multipart/form-data"> >
上传图片时候 前端要注意传输类型 并且是post的 get没办法提交文件 在url拼接
关于JSON的交互
用JSON不用HTML 解析方便 不占用大量的网络传输量
开发:
jar包
在Controller里面加个
@Responbody
然后 可以返回 对象类型了
页面获取到的是Json字符串!
@RequestMapping("getItem") @ResponseBody public Item getItem() { Item item =itemService.getItemById(3); return item; }
传入Json
为啥加了
@ResponseBody 可以实现json的响应支持?
原因是:
<!-- 配置注解驱动,相当于同时使用最新处理器映射跟处理器适配器,对json数据响应提供支持 同时使用自定义转换器Myconvert-->
<mvc:annotation-driven conversion-service="MyConvert"/>
传入JSON:
@RequestMapping("getItem") @ResponseBody public Item getItem(@RequestBody Item item) { System.out.println(item); item.setName("张三"); return item; }
出入的JSON 用Item类型的接受 字段是对应的 并且一定要加注解 @RequestBody
关于Restful 不是协议 也不是标准 是个风格!
//RESTful风格url上的参数通过{}点位符绑定
//点位符参数名与方法参数名不一致时,通过@PathVariable绑定
@RequestMapping("item/{id}") //url最后的参数值 被下面的括号中的id获取到 public String itemQuery(@PathVariable Integer id, Model model) { Item item =itemService.getItemById(id); model.addAttribute("item",item); return "itemEdit"; }
用个大括号做个占位符
如果名字不一致 需要稍加改动
@RequestMapping("item/{id}") //url最后的参数值 被下面的括号中的id获取到
public String itemQuery(@PathVariable(@PathVariable("id")) Integer idUse, Model model) {
注意区别: @RequestParam 的参数是通过 url?拼接来的
@PathVariable 是url后面占位符的 参数
访问:
http://localhost:8080/SSM01/item/3.action
这里需要加 .action的后缀
修改下Spring mvc 配置文件可以搞定
关于Spring mvc拦截器:
/** : 拦截所有请求 包括二级以上目录
拦截器:
package com.toov5.mvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class MyInterceptor implements HandlerInterceptor { public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("...afterCompletion..."); } public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println(".......postHandle......"); } public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("........preHandle......"); return true; //对拦截请求放行 true放行 } }
进行配置 告诉Spring mvc
<!-- 全局异常处理器配置 --> <bean class="com.toov5.mvc.utils.CustomerExceptionResolver"> </bean> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.toov5.mvc.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
Controller做标记(观察执行顺序):
@RequestMapping( value="itemList", method={RequestMethod.POST,RequestMethod.GET}) public ModelAndView itemList() { ModelAndView mav = new ModelAndView(); List<Item> itemList = itemService.getItemList(); mav.addObject("itemList",itemList); mav.setViewName("itemList"); //jsp的名字 System.out.println("ItemController.itemList************执行了****************"); return mav; }
访问后的执行结果:
注意这里修改了 日志的打印级别 否则打印的消息太细致 不容易观察:
详细介绍:
如果多个拦截器在一起呢?
再加一个拦截器 然后进行配置:
package com.toov5.mvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class MyInterceptor2 implements HandlerInterceptor { //方法执行后被执行 可以处理异常 或者清理资源 或者记录日志等操作 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("...afterCompletion..2..."); } //方法执行之后 同时可以获取到 ModelAndView 也就是返回视图之前 被执行 //设置页面的公用参数等操作 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println(".......postHandle..2......"); } //进入方法前被执行 登录拦截 权限校验 然后决定是否放行 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("........preHandle..2......"); return true; //对拦截请求放行 true放行 } }
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.toov5.mvc.interceptor.MyInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.toov5.mvc.interceptor.MyInterceptor2"></bean> </mvc:interceptor> </mvc:interceptors>
结果:
执行顺序 与 配置中的 顺序有关
一排人 钻山洞 钻进去 没出路 返回的过程了类似
如果第一个拦截器不放行: 只有这个方法执行了 目的地 网页也没有获取到
第一个放行 第二个不放行: 木有获取网页内容 拦截器只要放行 就会执行自己的 afterCompletion 不放行的是不被执行的 两个都执行才执行handler里面的方法
案例: 登录拦截器
如果没有登录 访问别的页面时候 会别拦截 跳转到 登录页面
如果登录了 当前回话里面 随便点击 随便跳转玩儿
拦截器负责拦截 这个请求 看看 session回话里面 有没有 登录信息标记 如果被标记了 就放行 如果没有那就拦截 跳转到登录页面
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 }/user/login.action"> 用户名:<input type="text" name="username" /><br> 密码:<input type="password" name="password" /><br> <input type="submit"> </form> </body> </html>
Controller:
package com.toov5.mvc.controller; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("user") public class UserController { @RequestMapping("toLogin") public String toLogin() { return"login"; } @RequestMapping("login") public String login(String username, String password,HttpSession session) { if (username.equals("admin")) { session.setAttribute("username", username); return "redirect:/itemList.action"; //user 下面没有这个请求地址 这个是根目录下面的所以 /需要加上 } return "login"; } }
拦截器:
package com.toov5.mvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor { //方法执行后被执行 可以处理异常 或者清理资源 或者记录日志等操作 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("...afterCompletion..2..."); } //方法执行之后 同时可以获取到 ModelAndView 也就是返回视图之前 被执行 //设置页面的公用参数等操作 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println(".......postHandle..2......"); } //进入方法前被执行 登录拦截 权限校验 然后决定是否放行 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { Object object = request.getSession().getAttribute("username"); if (object == null) { //这里要写全路径 response.sendRedirect(request.getContextPath()+"/user/toLogin.action"); } return true; //对拦截请求放行 true放行 } }
mvc配置:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.toov5.mvc.interceptor.MyInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.toov5.mvc.interceptor.MyInterceptor2"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <!-- 配置不拦截目录 --> <mvc:exclude-mapping path="/user/**"/> <!-- user打头的都不拦截 --> <bean class="com.toov5.mvc.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
后期补充了:
package com.toov5.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class HelloController { @RequestMapping("hello") public ModelAndView hello(HttpServletRequest request){ ////////////可以很灵活的使用 HttpServletRequest System.out.println("hello springmvc..."); ModelAndView mav = new ModelAndView(); //设置模型数据,用于传递到jsp //mav.addObject("msg", "hello springmvc......"); //设置视图名字,用于响应用户 request.setAttribute("msg", "httpservletrequest的内容"); mav.setViewName("/WEB-INF/jsp/hello.jsp"); return mav; } }