Spring Boot实践——统一异常处理

注解说明

  @ControllerAdvice,是Spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。让我们先看看@ControllerAdvice的实现:

/**
 * Specialization of {@link Component @Component} for classes that declare
 * {@link ExceptionHandler @ExceptionHandler}, {@link InitBinder @InitBinder}, or
 * {@link ModelAttribute @ModelAttribute} methods to be shared across
 * multiple {@code @Controller} classes.
 *
 * <p>Classes with {@code @ControllerAdvice} can be declared explicitly as Spring
 * beans or auto-detected via classpath scanning. All such beans are sorted via
 * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
 * AnnotationAwareOrderComparator}, i.e. based on
 * {@link org.springframework.core.annotation.Order @Order} and
 * {@link org.springframework.core.Ordered Ordered}, and applied in that order
 * at runtime. For handling exceptions, an {@code @ExceptionHandler} will be
 * picked on the first advice with a matching exception handler method. For
 * model attributes and {@code InitBinder} initialization, {@code @ModelAttribute}
 * and {@code @InitBinder} methods will also follow {@code @ControllerAdvice} order.
 *
 * <p>Note: For {@code @ExceptionHandler} methods, a root exception match will be
 * preferred to just matching a cause of the current exception, among the handler
 * methods of a particular advice bean. However, a cause match on a higher-priority
 * advice will still be preferred to a any match (whether root or cause level)
 * on a lower-priority advice bean. As a consequence, please declare your primary
 * root exception mappings on a prioritized advice bean with a corresponding order!
 *
 * <p>By default the methods in an {@code @ControllerAdvice} apply globally to
 * all Controllers. Use selectors {@link #annotations()},
 * {@link #basePackageClasses()}, and {@link #basePackages()} (or its alias
 * {@link #value()}) to define a more narrow subset of targeted Controllers.
 * If multiple selectors are declared, OR logic is applied, meaning selected
 * Controllers should match at least one selector. Note that selector checks
 * are performed at runtime and so adding many selectors may negatively impact
 * performance and add complexity.
 *
 * @author Rossen Stoyanchev
 * @author Brian Clozel
 * @author Sam Brannen
 * @since 3.2
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {

    /**
     * Alias for the {@link #basePackages} attribute.
     * <p>Allows for more concise annotation declarations e.g.:
     * {@code @ControllerAdvice("org.my.pkg")} is equivalent to
     * {@code @ControllerAdvice(basePackages="org.my.pkg")}.
     * @since 4.0
     * @see #basePackages()
     */
    @AliasFor("basePackages")
    String[] value() default {};

    /**
     * Array of base packages.
     * <p>Controllers that belong to those base packages or sub-packages thereof
     * will be included, e.g.: {@code @ControllerAdvice(basePackages="org.my.pkg")}
     * or {@code @ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"})}.
     * <p>{@link #value} is an alias for this attribute, simply allowing for
     * more concise use of the annotation.
     * <p>Also consider using {@link #basePackageClasses()} as a type-safe
     * alternative to String-based package names.
     * @since 4.0
     */
    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #value()} for specifying the packages
     * to select Controllers to be assisted by the {@code @ControllerAdvice}
     * annotated class.
     * <p>Consider creating a special no-op marker class or interface in each package
     * that serves no purpose other than being referenced by this attribute.
     * @since 4.0
     */
    Class<?>[] basePackageClasses() default {};

    /**
     * Array of classes.
     * <p>Controllers that are assignable to at least one of the given types
     * will be assisted by the {@code @ControllerAdvice} annotated class.
     * @since 4.0
     */
    Class<?>[] assignableTypes() default {};

    /**
     * Array of annotations.
     * <p>Controllers that are annotated with this/one of those annotation(s)
     * will be assisted by the {@code @ControllerAdvice} annotated class.
     * <p>Consider creating a special annotation or use a predefined one,
     * like {@link RestController @RestController}.
     * @since 4.0
     */
    Class<? extends Annotation>[] annotations() default {};

}

该注解使用@Component注解,这样的话当我们使用<context:component-scan>扫描时也能扫描到。

通过类型的描述,可以得知几点:

  1.     @ControllerAdvice是一个@Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
  2.     Spring4之前,@ControllerAdvice在同一调度的Servlet中协助所有控制器。Spring4已经改变:@ControllerAdvice支持配置控制器的子集,而默认的行为仍然可以利用。
  3.     在Spring4中, @ControllerAdvice通过annotations(), basePackageClasses(), basePackages()方法定制用于选择控制器子集

不过据经验之谈,只有配合@ExceptionHandler最有用,其它两个不常用。

如果单使用@ExceptionHandler,只能在当前Controller中处理异常。但当配合@ControllerAdvice一起使用的时候,就可以摆脱那个限制了。

实现方式

一、统一异常处理中心(Handler)

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;

/**
 * 统一异常处理
 * @ClassName: GlobalExceptionHandler 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年8月29日 下午2:19:42  
 *
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理所有不可知的异常
     * @Title: handleException 
     * @Description: TODO
     * @Date 2018年8月29日 下午2:21:10 
     * @author OnlyMate
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public JSONObject handleException(HttpServletRequest req, Exception e){
        logger.error("handleException ==> "+ e.getMessage(), e);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("resultCode", "9999");
        jsonObject.put("resultMsg", "系统异常");
        return jsonObject;
    }
    
    /**
     * 处理自定义异常(这里处理的异常是继承RunTimeException的自定义的异常)
     * @Title: handleBusinessException 
     * @Description: TODO
     * @Date 2018年8月29日 下午2:24:55 
     * @author OnlyMate
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(CustomBizRuntimeException.class)
    public JSONObject handleBusinessException(CustomBizRuntimeException e){
        logger.error("handleBusinessException ==> "+ e.getErrorMessage(), e);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("resultCode", "9999");
        jsonObject.put("resultMsg", "CustomBizRuntimeException 自定义业务异常类 ==> 系统异常");
        return jsonObject;
    }
    
    /**
     * 处理自定义异常(这里处理的异常是继承Exception的自定义的异常)
     * @Title: handleCommonException 
     * @Description: TODO
     * @Date 2018年8月29日 下午2:24:55 
     * @author OnlyMate
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(CustomCommonException.class)
    public JSONObject handleCommonException(CustomCommonException e){
        logger.error("handleCommonException ==> "+ e.getErrorMessage(), e);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("resultCode", "9999");
        jsonObject.put("resultMsg", "CustomCommonException 自定义普通异常类 ==> 系统异常");
        return jsonObject;
    }
}

二、自定义异常

CustomBizRuntimeException,继承RuntimeException,可用于涉及事务操作时抛该异常回滚事务

/**
 * 自定义业务异常类
 * @ClassName: CustomBizRuntimeException 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年9月1日 下午4:17:49  
 *
 */
public class CustomBizRuntimeException extends RuntimeException {
    private static final long serialVersionUID = -8021190492897458166L;
    private String errorMessage = "CustomBizRuntimeException 自定义业务异常类 ";
    
    public String getErrorMessage() {
        return this.errorMessage;
    }
}

CustomCommonException,普通自定义异常

/**
 * 自定义普通异常类
 * @ClassName: CustomCommonException 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年9月1日 下午4:17:49  
 *
 */
public class CustomCommonException extends Exception {
    private static final long serialVersionUID = -8021190492897458166L;
    private String errorMessage = "CustomCommonException 自定义普通异常类";
    
    public String getErrorMessage() {
        return this.errorMessage;
    }
}

三、编写抛出对应异常的Controller

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.onlymate.springboot.exception.CustomBizRuntimeException;
import com.onlymate.springboot.exception.CustomCommonException;

@Controller
@RequestMapping(value="/exception")
public class ExceptionController {
    private Logger logger = LoggerFactory.getLogger(HelloWorldController.class);
    
    /**
     * 抛出CustomCommonException异常,然后处理该异常
     * @Title: index1 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:42:58 
     * @author OnlyMate
     * @return
     * @throws CustomCommonException
     */
    @ResponseBody
    @RequestMapping("/index1")
    public String index1() throws CustomCommonException {
        try {
            String str = null;
            logger.info(str.toString());
        } catch (Exception e) {
            throw new CustomCommonException();
        }
        return "Success";
    }
    
    /**
     * 抛出CustomBizRuntimeException异常,然后处理该异常
     * @Title: index2 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:43:25 
     * @author OnlyMate
     * @return
     */
    @ResponseBody
    @RequestMapping("/index2")
    public String index2() {
        try {
            String str = null;
            logger.info(str.toString());
        } catch (Exception e) {
            throw new CustomBizRuntimeException();
        }
        return "Success";
    }
    
    /**
     * 抛出Exception异常,然后处理该异常
     * @Title: index3 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:43:49 
     * @author OnlyMate
     * @return
     */
    @ResponseBody
    @RequestMapping("/index3")
    public String index3() {
        String str = null;
        logger.info(str.toString());
        return "Success";
    }
}

效果图

一、抛出CustomCommonException异常效果图

页面访问:http://localhost:8088/springboot/exception/index1

二、抛出CustomBizRuntimeException异常效果图

页面访问:http://localhost:8088/springboot/exception/index2

三、抛出Exception异常效果图

页面访问:http://localhost:8088/springboot/exception/index3

 

猜你喜欢

转载自www.cnblogs.com/OnlyCT/p/9570887.html
今日推荐