抛出自定义异常时如何动态确定方法签名?
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;
}
我遵循的标准要求我记录引发异常的 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;
}