首先看一下常见的 Throwable 类html
官方Java 8 Throwable 官方介绍。 Throwable 类是 Java 语言中全部错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能经过 Java 虚拟机或者 Java throw 语句抛出。相似地,只有此类或其子类之一才能够是 catch 子句中的参数类型。出于对异常的编译时检查的目的,Throwable 和任何没有继承自 RuntimeException 或者 Error 的 Throwable 子类,那么则视为检查异常 (For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either RuntimeException or Error are regarded as checked exceptions.) 两个子类的实例,Error 和 Exception,一般用于指示发生了异常状况。一般,这些实例是在异常状况的上下文中新近建立的,所以包含了相关的信息(好比堆栈跟踪数据)。java
Error 是 Throwable 的子类,用于指示合理的应用程序不该该试图捕获的严重问题。大多数这样的错误都是异常条件。虽然 ThreadDeath 错误是一个“正规”的条件,但它也是 Error 的子类,由于大多数应用程序都不该该试图捕获它。 在执行该方法期间,无需在其 throws 子句中声明可能抛出可是未能捕获的 Error 的任何子类,由于这些错误多是不再会发生的异常条件。web
Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。spring
所以咱们知道在 Java 语言中,首先能够被抛出(throw)和捕获(cache)的是 Throwable 或 Throwable 的子类。Throwable 有两个直接子类 Error 和 Exception。 Error 是程序不该该去主动捕获的严重问题,Exception 是能够去捕获的。Throwable 和任何没有继承自 RuntimeException 或者 Error 的 Throwable 子类,都是编译期异常。一般分为编译期异常和运行期异常,运行期异常一般指 RuntimeException 及其子类。编译期异常须要显示处理,运行期异常不须要显示处理。异常如果不捕获致使的结果是线程的死亡。sql
举一个获取 InputStream 的例子,没什么业务,异常处理大多跟业务相关。这里暂时仅从语法角度来演示一下。json
编译期异常api
public InputStream selfHandlerException(){ InputStream inputStream = null; try { // 作业务 inputStream = new FileInputStream(""); } catch (FileNotFoundException e) { // 处理异常 e.printStackTrace(); } finally { // 有没有异常都干的工做 } return inputStream; }
public InputStream callHandlerException() throws FileNotFoundException { InputStream inputStream = new FileInputStream(""); return inputStream; }
运行期异常,能够不显示处理springboot
public void runTime() { throws new RuntimeException(); }
针对许多时候,咱们方法中须要保证在执行某段代码后确保会执行另外一段代码,而不能处理异常oracle
// 即相似这样的代码在 spring 源码中时长见到 try { // 执行某段代码 System.out.println("try up"); int a = 1/0; System.out.println("try down"); }catch (Exception e){ System.out.println("catch"); throw e; }finally { // 确保会执行的代码 System.out.println("finally"); }
// 咱们能够写这样的代码来处理 try { // 执行某段代码 System.out.println("try up"); int a = 1/0; System.out.println("try down"); }finally { // 确保会执行的代码 System.out.println("finally"); }
而这时问题来了,try finally 能捕获哪些异常呢?我找了下资料没有找到,使用 jd-gui 反编译不回来。我尝试在这里写了 throw new Throwable(""),这时会报编译异常,因此这里捕获的不是 Throwable,尝试了 Throwable 的两个直接子类 Error 和 Exceptin 是能够的。至于为何这么设计还没找到相关资料,我想应该是为了留出之后再定义的 Throwable 子类在此语句中须要额外处理吧。app
更详细的异常信息,官网异常处理
Spring boot 中默认处理方式
spring boot 官方文档 在 27.1.11 Error Handling 章节讲到,默认状况下,Spring Boot提供了一个 /error 映射处理全部的错误,并将其注册为servlet容器中的“全局”错误页面。 BasicErrorController 做为默认的响应处理程序。
在源码中即:经过 ErrorPageRegistrarBeanPostProcessor 的 postProcessBeforeInitialization(ErrorPageRegistry registry) 方法进行错误页注册;默认使用的是 Tomcat,经过 TomcatServletWebServerFactory 的 configureContext(Context context, ServletContextInitializer[] initializers) 方法进行配置。 BasicErrorController 提供了以下两个方法来处理错误信息。对于请求 accept-type=text/html 则使用第一个方法,不然使用第二个方法处理。
@RequestMapping(produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes( request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } @RequestMapping @ResponseBody public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); return new ResponseEntity<>(body, status); }
第一个方法默认返回一个注明,Httpcode 等信息。 第二个方法默认返回的 Json 数据内容大体以下:
{ "timestamp": 1520403098597, "status": 500, "error": "Internal Server Error", "exception": "java.lang.ArithmeticException", "message": "/ by zero", "path": "/demos/" }
能够选择新增处理方式
若是默认返回的信息不能知足咱们的需求,咱们能够采用如下几种方式定义本身的处理规则及返回数据。
// basePackageClasses = AcmeController.class 能够省略不写,默认全局 @ControllerAdvice(basePackageClasses = AcmeController.class) public class AcmeControllerAdvice extends ResponseEntityExceptionHandler { // 指明具体的异常,若不指明则默认所有 @ExceptionHandler(YourException.class) @ResponseBody ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = getStatus(request); return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status); } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } }
@Configuration public class GlobHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { return new ModelAndView(); } }
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.*; import java.io.IOException; import java.util.EnumSet; @Configuration public class ExceptionConfig { @Bean public FilterRegistrationBean myFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter()); registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class)); return registration; } } class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); // 这里能够捕获异常来处理 }catch (Exception e){ e.printStackTrace(); } } @Override public void destroy() { } }
建议方案
建议使用 @ControllerAdvice 来处理异常,首先解决所有异常的处理,即便用 @ExceptionHandler() 注解的方法;而后针对特定异常作对应处理。
使用阿里提供的代码检测工具检测到 @Transactional 时会要求明确写明 rollbackFor ,为何呢?来看下 rollbackFor 的做用
/** * Defines zero (0) or more exception {@link Class classes}, which must be * subclasses of {@link Throwable}, indicating which exception types must cause * a transaction rollback. * <p>By default, a transaction will be rolling back on {@link RuntimeException} * and {@link Error} but not on checked exceptions (business exceptions). See * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} * for a detailed explanation. * <p>This is the preferred way to construct a rollback rule (in contrast to * {@link #rollbackForClassName}), matching the exception class and its subclasses. * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}. * @see #rollbackForClassName * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ Class<? extends Throwable>[] rollbackFor() default {};
其中 "By default, a transaction will be rolling back on {@link RuntimeException} and {@link Error} but not on checked exceptions (business exceptions)" 这句比较有意思,说遇到 RuntimeException 及其子类 和 Error 会回滚,检查异常不会回滚。那么问题来了,根据上图可知, SQLException 是检查异常,难道执行 SQL 语句出错不会回滚吗?这显然不符合常理,再去看 Spring 文档
3.2.3. SQLExceptionTranslator SQLExceptionTranslator is an interface to be implemented by classes that can translate between SQLExceptions and Spring’s own org.springframework.dao.DataAccessException, which is agnostic in regard to data access strategy. Implementations can be generic (for example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error codes) for greater precision. SQLErrorCodeSQLExceptionTranslator is the implementation of SQLExceptionTranslator that is used by default. This implementation uses specific vendor codes. It is more precise than the SQLState implementation. The error code translations are based on codes held in a JavaBean type class called SQLErrorCodes. This class is created and populated by an SQLErrorCodesFactory which as the name suggests is a factory for creating SQLErrorCodes based on the contents of a configuration file named sql-error-codes.xml. This file is populated with vendor codes and based on the DatabaseProductName taken from the DatabaseMetaData. The codes for the actual database you are using are used.
可见 spring 将 SQLException 处理成 RuntimeException 的子类 DataAccessException 异常了。