Springmv 拦截器&异常处理

Posted rainbowpang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springmv 拦截器&异常处理相关的知识,希望对你有一定的参考价值。

拦截器

拦截器概念

拦截器的主要作用就是拦截用户的请求,在所匹配的目标方法之前进行执行。一般情况下,用作权限验证来判断用户是否登陆,还有就是商城中不登录不让购买也可以利用拦截器进行验证。

我们可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。 
并且实现以下方法: 
preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中可以对用户请求Request进行处理。需要注意的是这个方法的返回值,如果我们在该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果我们不再需要调用其他的组件去处理请求,则返回false。 
postHandle():*在业务处理器执行完目标方法后,但是是在DispatcherServlet向客户端返回响应前被调用*,在该方法中对用户请求Request进行处理。 
afterCompletion():这个方法在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

配置自定义的拦截器

二步走: 
第一步:编写自定义拦截器 
第二步:注册自定义拦截器

        <!--  配置自定义拦截器 实现 HandlerInterceptor-->

public class FirstIntercept implements HandlerInterceptor{

    /*  该方法在目标方法之前之前进行拦截调用
     * 若该方法返回true,则会继续调用后续的拦截器和目标方法
     * 若方法返回false,则会终止后续拦截器和目标方法的执行。
     * preHandle--》目标方法 --》postHandle--》afterCompletion
     * 
     * 权限 日志 事务
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        System.out.println("FirstIntercept  preHandle ");
        //return false;
        return true;
    }

    /*调用目标方法之后渲染视图之前
     * 可以对请求域中的属性或者视图做出修改
     * */
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler, ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("FirstIntercept  postHandle ");
    }

    /*渲染视图之后    
     *  释放资源        
     * */
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        System.out.println("FirstIntercept  afterCompletion ");
    }   
}

<!--  注册自定义拦截器  -->


    <mvc:interceptors>  
        <bean  class="com.wf.springmvc.crud.intercept.FirstIntercept"></bean>
        <bean class="com.wf.springmvc.crud.intercept.SecondIntercept"></bean>
    </mvc:interceptors>

    <!-- 配置自定义拦截器 方法二-->
    <mvc:interceptors>

        <!-- 配置拦截器(不)作用路径 是requestmapping路径 -->
        <mvc:interceptor>
        <!-- <mvc:exclude-mapping path="/list"/> -->
        <mvc:mapping path="/list"/>
            <bean class="com.wf.springmvc.crud.intercept.SecondIntercept"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

注意: 
在配置拦截器时,我们可以利用【mvc:interceptor】子节点,进行指定拦截器去作用在那些请求上面,例如上面的配置自定义拦截器 方法二 ,其实这样可以大大优化我们的性能,因为这样的话,我们可以指定在某些需要拦截器的目标方法上进行拦截,而对于不需要拦截的目标方法则不进行拦截。

拦截器执行顺序

对于 单个拦截器,拦截的顺序是按照下列顺序进行,但是要注意的是此时的preHandle的返回值是true,如果返回值为false,则执行完HandlerAdapter的目标方法 之后就直接进行渲染视图,而不再进行其他的处理 
FirstIntercept的 preHandle—-》HandlerAdapter的目标方法 —-》FirstIntercept的postHandle—-》DispatcherServlet的渲染视图 —-》FirstIntercept的afterCompletion

对于多个拦截器,执行顺序就像剥洋葱那样层层递进。是根据注册自定义拦截器的顺序进行执行。注意的是,下图中的停止是直接停止,连目标方法都不会执行。 
技术分享图片

异常处理

Springmvc 通过HandlerExceptionResolver接口 处理程序的异常,包括Handler映射,数据绑定以及目标方法执行时发生的异常。

HandlerExceptionResolver接口的实现类有以下几种,但是我们常用的是四个: 
技术分享图片

其中在我们配置了【mvc:annotation-driven】时,DispatcherServlet 会给我们装配以下的三个默认的异常实现类,加断点,查看【DispatcherServlet 】的this变量中的【handlerExceptionResolver】 
技术分享图片

ExceptionHandlerExceptionResolver

1、ExceptionHandlerExceptionResolver主要处理Handler中用@ExceptionHandler注解定义的方法。 
2、对于@ExceptionHandler注解的方法,如果是发生的NullPointException,但是在我们的声明异常有RuntimeException和Exception,那么此时会根据异常的最近继承关系,找继承深度最浅的那个,(就近原则) 。 
3、ExceptionHandlerMethodResolver内部如果找不到@ExceptionHandler注解的话,就会找@ControllerAdvice中的@ExceptionHandler注解方法。

一般情况下,在我们的运行中出现异常的话,直接会在页面进行报错显示,如果我们利用了我们的异常处理,就可以避免这样。

<!--前台链接 -->
<a href="TestExceptionHandlerExceptionResolver?i=2">Test ExceptionHandlerExceptionResolver </a> <br>

<!-- 后台处理-->
    @RequestMapping("TestExceptionHandlerExceptionResolver")
    public String TestExceptionHandlerExceptionResolver(@RequestParam("i") int i){
        System.out.println("result : "+10/i);
        return "success";
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当我们运行上述代码时,如果我们没有进行异常处理的话,跳转的页面如下:(我们可以吧传递参数设置为0,让其强制出错) 
技术分享图片

但是如果我们在这个方法下面新增一个异常处理方法,即被@ExceptionHandler注解修饰的方法。则运行效果如下: 
添加的代码:

    @ExceptionHandler(value={ArithmeticException.class,IOException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver(Exception ex){
        System.out.println("01----出异常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }

    @ExceptionHandler({ClassNotFoundException.class,ClassCastException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver02(Exception ex){
        System.out.println("02----出异常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

注意: 
1、上述我们定义了两个@ExceptionHandler修饰的方法,但是一定要注意两个方法的异常类型不能相同,这样做的目的,是为了测试,当我们抛出的异常不在这预定义的异常之中,会进行就近选择进行处理。

2、@ExceptionHandler 注解 可以指定异常,可以为多个

3、@ExceptionHandler 修饰的方法,不能使用Map作为与前台交互的数据存储, 如果希望将错误新城传递到前台,我们需要使用ModelAndView 作为视图返回值 在model中进行异常的交互与显示

4、@ExceptionHandler 方法有异常的优先级,一般都是匹配相似度较高的

5、如果出现异常在本类中找不到@ExceptionHandler修饰的方法进行异常处理,则会进入到 有@ControllerAdvice修饰的类中去查找@ExceptionHandler修饰的方法进行匹配

6、注意@ExceptionHandler可以匹配多个异常,但是在不同的方法中不能处理相同的异常,因为会不知道该用哪个方法进行处理,回报500异常

运行效果图如下: 
技术分享图片

或者我们单独写出来一个类,用来存放异常处理,如下:


@ControllerAdvice
public class HandlerExceptionController {
    @ExceptionHandler({ArithmeticException.class,IOException.class})
    public ModelAndView TestExceptionHandlerExceptionResolver(Exception ex){
        System.out.println("03----ControllerAdvice出异常了:"+ex);
        ModelAndView model = new ModelAndView("error");
        model.addObject("exception", ex);
        return model;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意使用类注解@ControllerAdvice和异常注解@ExceptionHandler。 
这样的话,当发生异常时,异常在本类中找不到@ExceptionHandler修饰的方法进行异常处理,则会进入到 有@ControllerAdvice修饰的类中去查找@ExceptionHandler修饰的方法进行匹配。

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver一般用于自己制定特定的响应状态和错误信息信息显示上面。一般我们在向外抛出异常进行处理时,我们可以抛出我们自己定义的异常类。 
在处理器方法中抛出了上述异常,若【ExceptionHandlerExceptionResolver】不解析我们的异常类,这是由于触发的自定义异常带有【@ResponseStatus注解】,因此,会被ResponseStatusExceptionResolver解析到。最后响应【@ResponseStatus注解】属性值给客户端。

<!-- 前台链接  -->

<a href="TestResponseStatusExceptionResolver?i=10">Test ResponseStatusExceptionResolver </a> <br>


<!-- 后台处理-->

    // 测试ResponseStatusExceptionResolver  注意自定义异常类NameNOTINPasswordException
    @RequestMapping("TestResponseStatusExceptionResolver")
    public String TestResponseStatusExceptionResolver(@RequestParam("i") int i){
        if(i==13 ){
            throw new NameNOTINPasswordException(); // 更改浏览器参数i为13
            //  throw new RuntimeErrorException(null);
        }
        System.out.println("TestResponseStatusExceptionResolver ...");
        return "success";
    }

            <!-- 自定义异常类 -->
//    reason指定显示信息   value http的状状态码  屏蔽此注解对比  拿到外部浏览器进行显示
//  不要标在方法上面,尽管可以,会造成应该正常的显示页面也产生错误页面上

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名与密码不匹配")
public class NameNOTINPasswordException extends RuntimeException {
    private static final long serialVersionUID = 1L;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

注意:ResponseStatusExceptionResolver只处理带有@ResponseStatus注解的异常并将其映射为状态码。 
第一次我们抛出RuntimeErrorException,由于没有注解@ResponseStatus,所以返回的为500 
第二次我们修改我们抛出的异常为自定义的异常,并且利用注解@ResponseStatus,所以返回我们特定的状态码和信息。 
技术分享图片

DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver这个主要是处理Spring一些特定的异常并且把他们转化为状态码。在源码中我们可以看到。

技术分享图片

测试上述HttpRequestMethodNotSupportedException。他的意思是不支持我们提交的方法方式。 
我们的超链接是GET方式,我们在后台处理时,将方法指定为处理POST请求,这样的话,就会交给我们的这个DefaultHandlerExceptionResolver进行处理。

<!-- 前台链接  get 方式 -->
<a href="TestHttpRequestMethodNotSupportedException">Test HttpRequestMethodNotSupportedException </a> <br>

<!--后台处理  post方式 -->
    @RequestMapping(value="/TestHttpRequestMethodNotSupportedException",method=RequestMethod.POST)
    public String TestHttpRequestMethodNotSupportedException(){
        System.out.println("TestHttpRequestMethodNotSupportedException...");
        return "success";

    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

技术分享图片

注意: 显然,从这里我们可以看出以前再出现错误页面时,都是经过框架给我们处理过的。

SimpleMappingExceptionResolver

这个异常处理,主要是解决当发生我们所指定的异常情况时,跳转到我们所指定的页面。这个异常我们在Springmvc.xml文件中进行注册。

<!-- 前台链接 -->
<a href="TestSimpleMappingExceptionResolver?i=10">Test SimpleMappingExceptionResolver </a> <br>

<!--后台处理 -->
@RequestMapping("/TestSimpleMappingExceptionResolver")
    public String TestSimpleMappingExceptionResolver(@RequestParam("i") Integer i){
        int [] vals = new int[20];
        System.out.println(vals[i]);  // 传递参数 21 让其发生 java.lang.ArrayIndexOutOfBoundsException
        return"success";
    }


<!--文件配置 -->

<!-- 配置异常  SimpleMappingExceptionResolver
            在这个页面中,会将异常信息存储到RequestScope域中的exception属性中,我们可以直接在页面获取
            或者我们也可以配置这个exceptionAttribute属性,将指定 RequestScope域中的特定属性中
    -->
    <bean class=" org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">  <!--name固定  -->
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>  
                <!-- 配置指定的异常到指定的位置 -->
            </props>
        </property>

        <property name="exceptionAttribute" value="ex"></property>
            <!-- 配置 特定的域 ,在页面应该是${ex}-->
    </bean>

<!-- error 页面 -->

<body>
Error Page <br>
默认存储域: ${exception }  <br>
haha   <br>
配置修改存储域:${ex}<br>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

注意:配置指定的异常到指定的位置,依然经过我们的视图解析器进行配置,所以要在指定位置编写错误页面。

 

以上是关于Springmv 拦截器&异常处理的主要内容,如果未能解决你的问题,请参考以下文章

Spring统一异常处理

异常和TCP通讯

Struts2自定义拦截器处理全局异常

Spring MVC统一异常处理

重温SSM框架系列9 - SpringMVC中的拦截器和异常处理

SpringMVC——拦截器异常处理机制