当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:
请求转发与重定向,两者区别如下:
forward:表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()
/**
* 处理器方法返回ModelAndView,实现转发forward
* 语法:setViewName("forward:视图文件完整路径")
* forward:不和视图解析器一同使用(忽略视图解析器)
*/
@RequestMapping("/doForward")
public ModelAndView doForward(){
ModelAndView mdv = new ModelAndView();
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC做web开发");
mv.addObject("fun","执行的是doForward方法");
//显示转发
//mdv.setViewName("forward:WEB-INF/jsp/show.jsp");
mdv.setViewName("forward:hello.jsp");
return mdv;
}
redirect:表示重定向,实现 response.sendRedirect("xx.jsp")
/**
* 处理器方法返回ModelAndView,实现重定向redirect
* 语法:setViewName("redirect:视图完整路径")
* redirect:不和视图解析器一同使用(忽略视图解析器)
*
* SpringMVC框架对重定向操作:
* 1、SpringMVC框架把Model中的简单类型数据转为string使用,作为hello.jsp的get请求参数使用
* 在doRedirect 和 hello.jsp 两次请求之间传递数据
* 2、在目标hello.jsp页面可以使用参数集合对象 ${param}获取请求参数值${param.myname}
*
* 注:重定向不能访问 /WEB-INF资源
*/
@RequestMapping("doRedirect")
public ModelAndView doRedirect(String name,Integer age){
ModelAndView mv = new ModelAndView();
//数据放入到 request作用域
mv.addObject("myname",name);
mv.addObject("myage",age);
//重定向
//http://localhost:8081/springmvc_8_forward_redirect/hello.jsp?myname=%E6%9D%8E%E5%9B%9B&myage=18
mv.setViewName("redirect:hello.jsp");
/**
* 重定向不能访问 /WEB-INF资源
* HTTP状态 404 - 未找到
* http://localhost:8081/springmvc_8_forward_redirect/WEB-INF/jsp/show.jsp?myname=%E6%9D%8E%E5%9B%9B&myage=18
* */
// mv.setViewName("redirect:WEB-INF/jsp/show.jsp");
return mv;
}
将异常处理方法专门定义在一个类中,作为全局的异常处理类
需要使用注解@ControllerAdvice(控制器增强),是给控制器对象增强功能的。
使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。
当使用@RequestMapping 注解修饰的方法抛出异常时,会执行@ControllerAdvice 修饰的类中的异常处理方法
@ControllerAdvice 是使用@Component 注解修饰的,可以
/**
* @ControllerAdvice : 控制器增强(给控制器类增加功能--异常处理功能)
* 位置:类上面
* 特点:在SpringMvc配置文件声明组件扫描器,指定@ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理异常方法和控制器方法定义一样,可以有多个参数,可以有ModelAndView,String, void,对象类型的返回值
*
* 形参:Exception,表示Controller中抛出的异常对象。
* 通过形参可以获取发生的异常信息。
*
* @ExceptionHandler (异常的class) : 表示异常的类型,当发生此类型异常时,
* 由当前方法处理
* */
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameExcepton(Exception exception){
/*
异常发生处理逻辑
1、需要把异常记录下来,记录到数据库,日志文件
记录法身时间,那个类、方法发生的,异常发生内容
2、发送通知,将异常信息通过邮件,短信发送给开发人员
3、给用户友好提示
* */
ModelAndView mv = new ModelAndView();
mv.addObject("msg","用户名必须是admin,其它用户不能访问");
mv.addObject("ex",exception);
mv.setViewName("nameError");
return mv;
}
//处理AgeException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","年龄不能大于120");
mv.addObject("ex",exception);
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exception){
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.addObject("msg","其它异常");
mv.addObject("ex",exception);
mv.setViewName("defaultError");
return mv;
}
}
自定义拦截器,需要实现 HandlerInterceptor 接口。而该接口中有三个方法
该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方
法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行
preHandle(request,response, Object handler):
该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。
由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修
改处理器方法的处理结果数据,且可以修改跳转方向。
postHandle(request,response, Object handler,modelAndView):
当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有
工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的
afterCompletion(request,response, Object handler, Exception ex):
拦截器中方法与处理器方法的执行顺序如下图
/**
* 拦截器类:拦截用户请求
*/
public class MyInterceptor implements HandlerInterceptor {
private long beginTime = 0;
/**
* preHandle():预处理方法
* 整个项目入口;
* 当preHandle返回true 请求可以被处理;preHandle返回false,请求到此方法就截止。
*
* 参数
* Object handler:被拦截的控制器对象
* 返回值 boolean
* true:请求通过了拦截器的验证,可以执行处理器方法
* 拦截器MyInterceptor的preHandle()
* =====执行MyController中的doSome方法=====
* 拦截器MyInterceptor的postHandle()
* 拦截器MyInterceptor的afterCompletion()
* false:请求没有通过拦截器的验证,请求到达拦截器就截止
* 拦截器MyInterceptor的preHandle()
*
* 特点:
* 1、方法在控制器方法(MyController的doSome)之前先执行
* 2、在这个方法中可以获取请求的信息,验证请求是否符合要求
* 可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)
* 如果验证失败,截断请求,请求不能被处理
* 如果验证成功,放行请求,此时控制器方法才能执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
beginTime = System.currentTimeMillis();
System.out.println("拦截器MyInterceptor的preHandle()");
//计算的业务逻辑,根据计算结果,返回true或者false
//给浏览器一个返回结果
//request.getRequestDispatcher("/tips.jsp").forward(request,response);
return true;
}
/**
* postHandle()方法:后处理方法
* 参数:
* Object handler:被拦截的处理器对象MyController
* ModelAndView mv:处理器方法的返回值
* 特点:
* 1、在处理器方法之后执行的(MyController.doSome())
* 2、能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
* 数据和视图,可以影响到最后的执行结果。
* 3、主要是对原来的执行结果进行二次修改
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("拦截器MyInterceptor的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("拦截器MyInterceptor的afterCompletion()");
long endTime = System.currentTimeMillis();
System.out.println("从preHandle到请求处理完成的时间:"+(endTime - beginTime));
}
}
在SpringMVC配置文件中,注册多个拦截器
执行顺序
输出如下:
/**
* 1、第一个拦截器preHandle=true , 第二个拦截器preHandle=true
* 111111拦截器MyInterceptor的preHandle()
* 222222拦截器MyInterceptor的preHandle()
* =====执行MyController中的doSome方法=====
* 222222拦截器MyInterceptor的postHandle()
* 111111拦截器MyInterceptor的postHandle()
* 222222拦截器MyInterceptor的afterCompletion()
* 111111拦截器MyInterceptor的afterCompletion()
*
* 2、第一个拦截器preHandle=true , 第二个拦截器preHandle=false
* 111111拦截器MyInterceptor的preHandle()
* 222222拦截器MyInterceptor的preHandle()
* 111111拦截器MyInterceptor的afterCompletion()
*
* 3、第一个拦截器preHandle=false , 第二个拦截器preHandle=true|false
* 111111拦截器MyInterceptor的preHandle()
*/