抛出自定义异常时如何动态确定方法签名?

How to determine a method signature dynamically when throwing custom exceptions?

我遵循的标准要求我记录引发异常的 class#method,以便从我们的 UI 轻松调试。无需提取日志,我就知道是什么方法产生了异常,并且可以(通常)轻松找到根本原因。

但是,我想要一种确定方法签名的动态方式,因为我目前正在对抛出异常的所有地方进行硬编码。有没有更好的办法。

 @Service
 public class UserService {

     @Autowired
     private UserRepository userRepository;

     public User findUser(Long id){

          if(id == null || id < 1){
               throw new BadRequestException("UserService#findUser", String.format(
                                             "Invalid Id: %d", id));
          }

          User user = userRepository.findOne(id);

          if(user == null){
               throw new DataNotFoundException("UserService#findUser", String.format(
                                               "Can't Find User for Id: %d", id));
          }
          return user;
     }
 }

--

目标是我可以创建任何自定义异常,并且在该构造函数中我可以根据创建异常的位置推断方法签名。

 public BadRequestException(String issue){
      super(String.format("%s | Bad Request | %s", <<signature>>, issue);
 }

--

 UserService class --> findUser method --> BadRequest("UserService#findUser")
 UserService class --> createUser method --> BadRequest("UserService#createUser")

使用Throwable.getStackTrace()。这将 return 一个 StackTraceElement 数组,第一个条目将指向 Throwable 实例化的位置。

您可以引入一个辅助方法,从当前线程中检索方法和 class 名称:

private static MethodAndClassName retrieveMethodAndClassName() {
       StackTraceElement[] allStackTraces = Thread.currentThread().getStackTrace();
       StackTraceElement currentMethod = allStackTraces[2]; 
       return new MethodAndClassName(currentMethod.getMethodName(), currentMethod.getClassName());      
}

请注意,方法调用是堆叠的。我得到倒数第三个嵌套调用 (allStackTraces[2]),因为最后一个调用 (allStackTraces[0]) 是 Thread.getStackTrace(),前一个调用 (allStackTraces[1]) 是 retrieveMethodAndClassName() 并且最后一次调用之前的之前 (allStackTraces[2]) 就是您要查找的内容:调用 retrieveMethodAndClassName() 的方法。

MethodAndClassName 是自定义的 class,它定义了需要跟踪的信息:

public class MethodAndClassName {

    private String method;
    private String clazz;

    public MethodAndClassName(String method, String clazz) {
        this.method = method;
        this.clazz = clazz;
    }

    public String getMethodName() {
        return method;
    }

    public String getClassName() {
        return clazz;
    }
}

并在您的客户端代码中使用它,例如:

 public User findUser(Long id){

      if(id == null || id < 1){             
           throw new BadRequestException(ReflectionHelper.retrieveMethodAndClassName(), String.format(
                                         "Invalid Id: %d", id));
      }

      User user = userRepository.findOne(id);

      if(user == null){
           throw new DataNotFoundException(ReflectionHelper.retrieveMethodAndClassName(), String.format(
                                           "Can't Find User for Id: %d", id));
      }
      return user;
 }