2018年1月2日 星期二

攔截器、例外處理 (Spring3.x MVC 九)

※攔截器

※HandlerInterceptor

public class HelloInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("A1");
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("A2");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("A3");
    }
}

※寫四支一模一樣的,分別是 ABCD 的 1~3

※preHandle 執行在目標方法之前
postHandle 執行在目標方法之後、view 之前
afterCompletion view 之後

※preHandle 回傳 false,那之後的都不會執行


※spring 設定


mapping 是映射什麼路徑
exclude-mapping 是排除路徑

<mvc:interceptors>
    <bean class="init.HelloInterceptor" />
    
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="init.HelloInterceptor2" />
    </mvc:interceptor>
    
    <mvc:interceptor>
        <mvc:mapping path="/ooo/xxx/a/b/*.mvc" />
        <bean class="init.HelloInterceptor3" />
    </mvc:interceptor>
    
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/ooo/xxx/a/*/*.mvc" />
        <bean class="init.HelloInterceptor4" />
    </mvc:interceptor>
</mvc:interceptors>
    
<mvc:view-controller path="ooo/xxx/b/a/c.mvc" view-name="jump1" />
<mvc:view-controller path="ooo/xxx/a/b/c1.mvc" view-name="jump1" />
<mvc:view-controller path="ooo/xxx/a/b/c2.mvc" view-name="jump1" />

※可以不加 <mvc:annotation-driven />

※HelloInterceptor、HelloInterceptor2 可以說是一樣的

※注意 exclude-mapping 最上面一定要有 mapping,否則會報錯,也就是先指定一個大範圍,然後排除,排除可以寫好幾個


※測試

<a href="ooo/xxx/b/a/c.mvc">c</a>
<a href="ooo/xxx/a/b/c1.mvc">c1</a>
<a href="ooo/xxx/a/b/c2.mvc">c2</a>

※第一個執行 ABD,其他兩個都是 ABC


※執行順序

preHandle:寫在設定檔的前面最先執行,全部的 preHandle執行完才執行 postHandle
postHandle:寫在設定檔的前面最後執行,全部的 postHandle 執行完才執行 afterCompletion
afterCompletion :寫在設定檔的前面最後執行

preHandle 是正序;postHandle、afterCompletion 執行順序是倒序



※例外處理

使用 @ExceptionHandler

※controller

@RequestMapping("exception")
public String testException(Integer i) {
    System.out.println(2/i);
    return "hello";
}
    
@ExceptionHandler(ArithmeticException.class)
public String exceptionMethod() {
    System.out.println("我錯了");
    return "errPage";
}

※回傳的字串是配合 spring 設定檔的 InternalResourceViewResolver

※測試

<a href="ooo/xxx/exception.mvc?i=0">exception</a>

※網址是不變的



※不能使用 Map

@ExceptionHandler(ArithmeticException.class)
public ModelAndView exceptionMethod(Exception ex /*, Model model*/) {
    System.out.println("我錯了" + ex);
    ModelAndView mav = new ModelAndView("errPage");
    mav.addObject("excep", ex);
    return mav;
}

※當然包括子類 Model,只是放在參數裡,都還沒有 put,就 500了,想塞值到前端,要用 ModelAndView


※多個 @ExceptionHandler

@ExceptionHandler(ArithmeticException.class)
public String exceptionMethod(Exception ex) {
    System.out.println("A");
    return "errPage";
}
    
@ExceptionHandler(Exception.class)
public String exceptionMethod2(Exception ex) {
    System.out.println("B");
    return "errPage";
}

※會以完全匹配為主,沒有完全匹配才會退而求其次


※全域的例外處理 @ControllerAdvice

@ControllerAdvice
public class GlobalException {
    @ExceptionHandler(ArithmeticException.class)
    public String exceptionMethod(Exception ex) {
        System.out.println("C");
        return "errPage";
    }
    
    @ExceptionHandler(Exception.class)
    public String exceptionMethod2(Exception ex) {
        System.out.println("D");
        return "errPage";
    }
}

※區域例外處理全部都沒有 @ExceptionHandler 才會執行全域的  @ControllerAdvice

※全域必需設定
1. <context:component-scan base-package="controller" />
    屬性 use-default-filters 不能是 false,預設是 true
2. <mvc:annotation-driven />
否則有寫也不會執行



※預設的例外處理

看原碼 DefaultHandlerExceptionResolver.doResolveException,Eclipse 可使用 Ctrl + Shift + T
以 3.2.13 版為例,會發現有 13 個預設的例外處理
NoSuchRequestHandlingMethodException
HttpRequestMethodNotSupportedException
HttpMediaTypeNotSupportedException
HttpMediaTypeNotAcceptableException
MissingServletRequestParameterException
ServletRequestBindingException
ConversionNotSupportedException
TypeMismatchException
HttpMessageNotReadableException
HttpMessageNotWritableException
MethodArgumentNotValidException
MissingServletRequestPartException
BindException

※上圖為預設的,下圖為修改過的,想修改就必需如下設定


※自訂例外

@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "我錯了")
public class DefinedSelfException extends RuntimeException {}

※HttpStatus 有很多,隨便選即可

※controller

// @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "我錯了2")
@RequestMapping("definedSelfExp")
public String definedSelfExp(Integer i) {
    if (i == 0) throw new DefinedSelfException();
    
    System.out.println(2 / i);
    return "hello";
}

※@ResponseStatus 若寫在方法上,i == 0 時不變;但若不是 0,不會跳頁,會產生例外,但寫在這裡的中文會亂碼,寫在另外一支 class 不會,這個我沒解

※測試

<a href="ooo/xxx/definedSelfExp.mvc?i=0">definedSelfExp</a>





※SimpleMappingExceptionResolver


※spring 設定檔

<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>    
            <prop key="java.lang.ArithmeticException">arithmeticPage</prop>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">arrayIndexPage</prop>
        </props>
    </property>
    <property name="exceptionAttribute" value="xxx" />
</bean>
    
<mvc:default-servlet-handler />
    
<mvc:annotation-driven />

※prop 包起來的字串是頁面名稱,配合 InternalResourceViewResolver

※exceptionAttribute 如果不寫,預設是 exception,可以在 JSP 用 EL


※controller

@RequestMapping("simpleExp")
// @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "我錯了2")
public String simpleExp(Integer i) {
    // if (i == 0) throw new DefinedSelfException();
    System.out.println(2 / i);
    return "hello";
}

※方法上的 @ResponseStatus 是有用的


※測試

<a href="ooo/xxx/simpleExp.mvc?i=0">simpleExp</a>



沒有留言:

張貼留言