/******************************************************************************* * Copyright (c) 2005, 2017 springside.github.io * * Licensed under the Apache License, Version 2.0 (the "License"); *******************************************************************************/ package org.springside.modules.utils.base; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springside.modules.utils.base.annotation.Nullable; import com.google.common.base.Throwables; /** * 关于异常的工具类. * * 1. 若干常用函数. * * 2. StackTrace性能优化相关,尽量使用静态异常避免异常生成时获取StackTrace,及打印StackTrace的消耗 * * @author calvin */ public class ExceptionUtil { private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; /** * 将CheckedException转换为RuntimeException重新抛出, 可以减少函数签名中的CheckExcetpion定义. * * CheckedException会用UndeclaredThrowableException包裹,RunTimeException和Error则不会被转变. * * from Commons Lange 3.5 ExceptionUtils. * * 虽然unchecked()里已直接抛出异常,但仍然定义返回值,方便欺骗Sonar。因此本函数也改变了一下返回值 * * 示例代码: * * <pre> * try{ ... }catch(Exception e){ throw unchecked(t); } * * @see ExceptionUtils#wrapAndThrow(Throwable) */ public static RuntimeException unchecked(Throwable t) { if (t instanceof RuntimeException) { throw (RuntimeException) t; } if (t instanceof Error) { throw (Error) t; } throw new UncheckedException(t); } /** * 如果是著名的包裹类,从cause中获得真正异常. 其他异常则不变. * * Future中使用的ExecutionException 与 反射时定义的InvocationTargetException, 真正的异常都封装在Cause中 * * 前面 unchecked() 使用的UncheckedException同理. * * from Quasar and Tomcat's ExceptionUtils */ public static Throwable unwrap(Throwable t) { if (t instanceof java.util.concurrent.ExecutionException || t instanceof java.lang.reflect.InvocationTargetException || t instanceof UncheckedException) { return t.getCause(); } return t; } /** * 组合unchecked与unwrap的效果 */ public static RuntimeException uncheckedAndWrap(Throwable t) { Throwable unwrapped = unwrap(t); if (unwrapped instanceof RuntimeException) { throw (RuntimeException) unwrapped; } if (unwrapped instanceof Error) { throw (Error) unwrapped; } throw new UncheckedException(unwrapped); } /** * 将StackTrace[]转换为String, 供Logger或e.printStackTrace()外的其他地方使用. * * @see Throwables#getStackTraceAsString(Throwable) */ public static String stackTraceText(Throwable t) { return Throwables.getStackTraceAsString(t); } /** * 获取异常的Root Cause. * * 如无底层Cause, 则返回自身 * * @see Throwables#getRootCause(Throwable) */ public static Throwable getRootCause(Throwable t) { return Throwables.getRootCause(t); } /** * 判断异常是否由某些底层的异常引起. */ @SuppressWarnings("unchecked") public static boolean isCausedBy(Throwable t, Class<? extends Exception>... causeExceptionClasses) { Throwable cause = t; while (cause != null) { for (Class<? extends Exception> causeClass : causeExceptionClasses) { if (causeClass.isInstance(cause)) { return true; } } cause = cause.getCause(); } return false; } /** * 拼装 短异常类名: 异常信息. * * 与Throwable.toString()相比使用了短类名 * * @see ExceptionUtils#getMessage(Throwable) */ public static String toStringWithShortName(@Nullable Throwable t) { return ExceptionUtils.getMessage(t); } /** * 拼装 短异常类名: 异常信息 <-- RootCause的短异常类名: 异常信息 */ public static String toStringWithRootCause(@Nullable Throwable t) { if (t == null) { return StringUtils.EMPTY; } final String clsName = ClassUtils.getShortClassName(t, null); final String message = StringUtils.defaultString(t.getMessage()); Throwable cause = getRootCause(t); StringBuilder sb = new StringBuilder(128).append(clsName).append(": ").append(message); if (cause != t) { sb.append("; <---").append(toStringWithShortName(cause)); } return sb.toString(); } /////////// StackTrace 性能优化相关//////// /** * from Netty, 为静态异常设置StackTrace. * * 对某些已知且经常抛出的异常, 不需要每次创建异常类并很消耗性能的并生成完整的StackTrace. 此时可使用静态声明的异常. * * 如果异常可能在多个地方抛出,使用本函数设置抛出的类名和方法名. * * <pre> * private static RuntimeException TIMEOUT_EXCEPTION = ExceptionUtil.setStackTrace(new RuntimeException("Timeout"), * MyClass.class, "mymethod"); * * </pre> */ public static <T extends Throwable> T setStackTrace(T exception, Class<?> throwClass, String throwClazz) { exception.setStackTrace( new StackTraceElement[] { new StackTraceElement(throwClass.getName(), throwClazz, null, -1) }); return exception;// NOSONAR } /** * 清除StackTrace. 假设StackTrace已生成, 但把它打印出来也有不小的消耗. * * 如果不能控制StackTrace的生成,也不能控制它的打印端(如logger),可用此方法暴力清除Trace. * * 但Cause链依然不能清除, 只能清除每一个Cause的StackTrace. */ public static <T extends Throwable> T clearStackTrace(T exception) { Throwable cause = exception; while (cause != null) { cause.setStackTrace(EMPTY_STACK_TRACE); cause = cause.getCause(); } return exception;// NOSONAR } /** * 适用于Message经常变更的异常, 可通过clone()不经过构造函数的构造异常再设定新的异常信息 */ public static class CloneableException extends Exception implements Cloneable { private static final long serialVersionUID = -6270471689928560417L; protected String message; public CloneableException() { super((Throwable) null); } public CloneableException(String message) { super((Throwable) null); this.message = message; } public CloneableException(String message, Throwable cause) { super(cause); this.message = message; } @Override public CloneableException clone() { try { return (CloneableException) super.clone(); } catch (CloneNotSupportedException e) {// NOSONAR return null; } } public CloneableException clone(String message) { CloneableException newException = this.clone(); newException.setMessage(message); return newException; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public CloneableException setStackTrace(Class<?> throwClazz, String throwMethod) { ExceptionUtil.setStackTrace(this, throwClazz, throwMethod); return this; } } /** * 重载fillInStackTrace()方法,不生成StackTrace. * * 适用于Message经常变更,不能使用静态异常时 */ public static class CloneableRuntimeException extends RuntimeException implements Cloneable { private static final long serialVersionUID = 3984796576627959400L; protected String message; public CloneableRuntimeException() { super((Throwable) null); } public CloneableRuntimeException(String message) { super((Throwable) null); this.message = message; } public CloneableRuntimeException(String message, Throwable cause) { super(cause); this.message = message; } @Override public CloneableRuntimeException clone() { try { return (CloneableRuntimeException) super.clone(); } catch (CloneNotSupportedException e) { // NOSONAR return null; } } public CloneableRuntimeException clone(String message) { CloneableRuntimeException newException = this.clone(); newException.setMessage(message); return newException; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public CloneableRuntimeException setStackTrace(Class<?> throwClazz, String throwMethod) { ExceptionUtil.setStackTrace(this, throwClazz, throwMethod); return this; } } /** * 自定义一个CheckedException的wrapper * * @author calvin * */ public static class UncheckedException extends RuntimeException { private static final long serialVersionUID = 4140223302171577501L; public UncheckedException(Throwable cause) { super(cause); } @Override public String getMessage() { return super.getCause().getMessage(); } } }