当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器html
注意,对于请求转发的页面,能够是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。由于重定向至关于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的java
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操做进行了封装。如今可使用简单的方式实现转发和重定向。web
forward:表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()spring
redirect:表示重定向,实现 response.sendRedirect("xxx.jsp")数据库
处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:,且此时的视图再也不与视图解析器一同工做,这样能够在配置了解析器时指定不一样位置的视图。视图页面必须写出相对于项目根的路径。forward 操做不须要视图解析器。
处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径数组
仍是第一个案例中浏览器
/** * 处理器方法返回ModelAndView,实现转发forward * 语法: setViewName("forward:视图文件完整路径") * forward特色: 不和视图解析器一同使用,就当项目中没有视图解析器,不受视图解析器的限制了 */ @RequestMapping(value = "/doForward.do") public ModelAndView doForward(String name , Integer age){ ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); // mv.setViewName("show"); //显式转发 // mv.setViewName("forward:/WEB-INF/view/show.jsp"); // 配置了视图解析器,可是这个文件不在view目录之下就可使用这样的方法,由于forward不受视图解析器的限制 mv.setViewName("forward:/hello.jsp"); return mv; }
在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转
spring-mvc
/** * 处理器方法返回ModelAndView,实现重定向redirect * 语法:setViewName("redirect:视图完整路径") * redirect特色: 不和视图解析器一同使用,就当项目中没有视图解析器 * * 框架对重定向的操做: * 1.框架会把Model中的简单类型的数据,转为string使用,做为hello.jsp的get请求参数使用。 * 目的是在 doRedirect.do 和 hello.jsp 两次请求之间传递数据 * * 2.在目标hello.jsp页面可使用参数集合对象 ${param}获取请求参数值 * ${param.myname} * * 3.重定向不能访问/WEB-INF资源 */ @RequestMapping(value = "/doRedirect.do") public ModelAndView doRedirect(String name , Integer age){ ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); //重定向 mv.setViewName("redirect:/hello.jsp"); // 至关于这样,内部做为get请求,经过param能够获取的到 //http://localhost:8080/08_forword/hello.jsp?myname=lisi&myage=22 //重定向不能访问/WEB-INF资源 // mv.setViewName("redirect:/WEB-INF/view/show.jsp"); return mv; }
SpringMVC 框架处理异常的经常使用方式:使用@ExceptionHandler 注解处理异常tomcat
使用注解@ExceptionHandler 能够将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常服务器
而被注解的方法,其返回值能够是 ModelAndView、String,或 void,方法名随意,方法参数能够是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值
MyUserException是父类
package com.md.exception; /** * @author MD * @create 2020-08-14 20:31 */ public class MyUserException extends Exception { public MyUserException() { super(); } public MyUserException(String message) { super(message); } } //------------------------------- package com.md.exception; /** * 当用户的年龄有异常抛出AgeException * @author MD * @create 2020-08-14 20:33 */ public class AgeException extends MyUserException { public AgeException() { super(); } public AgeException(String message) { super(message); } } //---------------------------- package com.md.exception; /** * 当用户的姓名有异常抛出NameException * @author MD * @create 2020-08-14 20:32 */ public class NameException extends MyUserException { public NameException() { super(); } public NameException(String message) { super(message); } }
/** *此时抛出的异常是下面两个异常的父类 */ @RequestMapping(value = "/some.do") public ModelAndView doSome(String name , Integer age) throws MyUserException { ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); // 根据请求参数抛出异常 if (!"md".equals(name)){ throw new NameException("姓名不正确"); } if (age == null || age > 100){ throw new AgeException("年龄有误"); } mv.setViewName("show"); return mv; }
package com.md.handler; import com.md.exception.AgeException; import com.md.exception.NameException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; /** * @author MD * @create 2020-08-14 20:38 */ /** * @ControllerAdvice : 控制器加强(也就是说给控制器类增长功能--异常处理功能) * 位置:在类的上面。 * 特色:必须让框架知道这个注解所在的包名,须要在springmvc配置文件声明组件扫描器。 * 指定@ControllerAdvice所在的包名 */ @ControllerAdvice public class GlobalException { //定义方法,处理发生的异常 /* 处理异常的方法和控制器方法的定义同样, 能够有多个参数,能够有ModelAndView, String, void,对象类型的返回值 形参:Exception,表示Controller中抛出的异常对象。 经过形参能够获取发生的异常信息。 @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时, 由当前方法处理 */ @ExceptionHandler(value = NameException.class) public ModelAndView doNameException(Exception ex) { // 处理NameException异常 /* 异常发生处理逻辑: 1.须要把异常记录下来, 记录到数据库,日志文件。 记录日志发生的时间,哪一个方法发生的,异常错误内容。 2.发送通知,把异常的信息经过邮件,短信,微信发送给相关人员。 3.给用户友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg", "姓名是md,其余用户不能访问"); mv.addObject("ex", ex); mv.setViewName("nameError"); return mv; } @ExceptionHandler(value = AgeException.class) public ModelAndView doAgeException(Exception ex) { // 处理AgeException异常 /* 异常发生处理逻辑: 1.须要把异常记录下来, 记录到数据库,日志文件。 记录日志发生的时间,哪一个方法发生的,异常错误内容。 2.发送通知,把异常的信息经过邮件,短信,微信发送给相关人员。 3.给用户友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg", "年龄过大"); mv.addObject("ex", ex); mv.setViewName("AgeError"); return mv; } // 这个只能有一个,也就是万能的异常处理方法 // 处理其余异常,NameException、AgeException以外的异常,也就是除了自定义以外的异常都能处理 @ExceptionHandler public ModelAndView doOtherException(Exception ex) { // 处理OtherException异常 ModelAndView mv = new ModelAndView(); mv.addObject("msg", "404"); mv.addObject("ex", ex); mv.setViewName("defaultError"); return mv; } }
nameError.jsp
<%-- Created by IntelliJ IDEA. User: MD Date: 2020/8/14 Time: 20:54 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>nameError.jsp</h1> <h2>提示:${msg}</h2> <h2>系统异常信息:${ex.message}</h2> </body> </html>
<h1>ageError.jsp</h1> <h2>提示:${msg}</h2> <h2>系统异常信息:${ex.message}</h2> <%--和上面都同样,这里就省略了--%> <h1>defaultError.jsp</h1> <h2>提示:${msg}</h2> <h2>系统异常信息:${ex.message}</h2>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--声明组件扫描器--> <context:component-scan base-package="com.md.controller"/> <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:视图文件的路径--> <property name="prefix" value="/WEB-INF/view/" /> <!--后缀:视图文件的扩展名--> <property name="suffix" value=".jsp"/> </bean> <!-- 处理异常的两个步骤 1. 注册组件扫描器,也便是@ControllerAdvice注解所在的包名 2. 注册注解驱动, --> <context:component-scan base-package="com.md.handler"/> <mvc:annotation-driven/> </beans>
这样就能够了
SpringMVC 中的 Interceptor 拦截器是很是重要和至关有用的,它的主要做用是拦截指定的用户请求,并进行相应的预处理与后处理。
Controller类
@RequestMapping(value = "/some.do") public ModelAndView doSome(String name , Integer age) { System.out.println("-------MyController的doSome()"); ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); mv.setViewName("show"); return mv; }
仍是在以前的项目中,定义个普通类,做为拦截器
package com.md.handler; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Date; /** * 拦截器类,拦截用户的请求 * @author MD * @create 2020-08-14 21:18 */ public class MyInterceptor implements HandlerInterceptor { // 快捷键:ctrl + O private long btime = 0; /* * preHandle叫作预处理方法。 * 重要:是整个项目的入口,门户。 当preHandle返回true 请求能够被处理。 * preHandle返回false,请求到此方法就截止。 * * 参数: * Object handler : 被拦截的控制器对象 * 返回值boolean * true:请求是经过了拦截器的验证,能够执行处理器方法 * 执行顺序 拦截器: preHandle() -------MyController的doSome() 拦截器: postHandle() 拦截器: afterCompletion() * * false:请求没有经过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理 * 仅仅输出这一句话, * 拦截器: preHandle() * * * 特色: * 1.方法在控制器方法(MyController的doSome)以前先执行的。 * 用户的请求首先到达此方法 * * 2.在这个 方法中能够获取请求的信息, 验证请求是否符合要求。 * 能够验证用户是否登陆, 验证用户是否有权限访问某个链接地址(url)。 * 若是验证失败,能够截断请求,请求不能被处理。 * 若是验证成功,能够放行请求,此时控制器方法才能执行。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { btime = System.currentTimeMillis(); System.out.println("拦截器: preHandle()"); // 计算的业务逻辑,根据计算的结果,返回true或者false // 给浏览器一个反馈 // request.getRequestDispatcher("/tips.jsp").forward(request,response); // // return false; return true; } /* postHandle:后处理方法。 参数: Object handler:被拦截的处理器对象MyController ModelAndView modelAndView:处理器方法的返回值 特色: 1.在处理器方法以后执行的(MyController.doSome()) 2.可以获取处处理器方法的返回值ModelAndView,能够修改ModelAndView中的 数据和视图,能够影响到最后的执行结果。 3.主要是对原来的执行结果作二次修正, ModelAndView mv = MyController.doSome(); postHandle(request,response,handler,mv); */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("拦截器: postHandle()"); // 对原来的doSome执行的结果进行调整 if (modelAndView != null){ modelAndView.addObject("mydate" , new Date()); modelAndView.setViewName("other"); } } /* afterCompletion:最后执行的方法 参数 Object handler:被拦截器的处理器对象 Exception ex:程序中发生的异常 特色: 1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。 2.通常作资源回收工做的, 程序请求过程当中建立了一些对象,在这里能够删除,把占用的内存回收。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("拦截器: afterCompletion()"); long etime = System.currentTimeMillis(); System.out.println("计算从preHandle到请求处理完成的时间:"+(etime - btime )); } }
建立springmvc的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--声明组件扫描器--> <context:component-scan base-package="com.md.controller"/> <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:视图文件的路径--> <property name="prefix" value="/WEB-INF/view/" /> <!--后缀:视图文件的扩展名--> <property name="suffix" value=".jsp"/> </bean> <!-- 声明拦截器: 拦截器能够一个或者多个--> <mvc:interceptors> <!--声明第一个拦截器--> <mvc:interceptor> <!-- 指定拦截的请求url地址, page就是url地址,可使用通配符 ** , 表示任意的字符,文件或者多级目录--> <!-- http://localhost:8080/user/xxx--> <!--<mvc:mapping path="/user/**"/>--> <mvc:mapping path="/**"/> <!--声明拦截器对象--> <bean class="com.md.handler.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans>
拦截器中方法与处理器方法的执行顺序
再定义一个拦截器
package com.md.handler; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("111111111111拦截器: preHandle()"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("11111111111111111拦截器: postHandle()"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("111111111111111拦截器: afterCompletion()"); } } //--------------------------------------- package com.md.handler; public class MyInterceptor2 implements HandlerInterceptor { // 快捷键:ctrl + O @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("2222222222222222拦截器: preHandle()"); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("2222222222222222拦截器: postHandle()"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("122222222222222222拦截器: afterCompletion()"); } } //---------------------------------------
多个拦截器的注册,在springmvc的配置文件
<!-- 声明拦截器: 拦截器能够一个或者多个 在框架中保存是在一个ArrayList , 先声明的在前面, --> <mvc:interceptors> <!--声明第一个拦截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--声明拦截器对象--> <bean class="com.md.handler.MyInterceptor"/> </mvc:interceptor> <!--声明第二个拦截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--声明拦截器对象--> <bean class="com.md.handler.MyInterceptor2"/> </mvc:interceptor> </mvc:interceptors> <!-- 返回的都是true,执行的顺序--> <!--111111111111拦截器: preHandle()--> <!--2222222222222222拦截器: preHandle()--> <!-- -------MyController的doSome()--> <!--2222222222222222拦截器: postHandle()--> <!--11111111111111111拦截器: postHandle()--> <!--122222222222222222拦截器: afterCompletion()--> <!--111111111111111拦截器: afterCompletion()--> <!-- 第一个true,第一个false,执行顺序--> <!--111111111111拦截器: preHandle()--> <!--2222222222222222拦截器: preHandle()--> <!--111111111111111拦截器: afterCompletion()--> <!-- 返回的都是false,执行的顺序--> <!--111111111111拦截器: preHandle()--> </beans>
当有多个拦截器时,造成拦截器链。拦截器链的执行顺序,与其注册顺序一致。须要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法