Использование аннотаций для обработки исключений?
предположим, у меня есть метод, который выдает какое-то исключение. Код исключения находится в сторонней библиотеке, которая обращается к внешней службе. У меня есть несколько классов, которые много работают с внешними службами, и есть много обработки исключений для решения потенциальных проблем. Проблема в том, что у меня может быть много исключений, но мне может потребоваться выполнить только одно из нескольких действий, если есть один, и есть тонна блоков try/catch засыпали около. Тип исключения может даже не быть релевантным, или разные методы могут вызывать один и тот же тип исключения, но в зависимости от метода должны быть предприняты различные действия.
то, что я ищу, - это аннотация, которая может заменить try/catch и просто диктовать поведение, которое должно быть принято, когда в этом методе есть исключение. Я знаю, что Spring ApsectJ может делать такие вещи, но в настоящее время я не могу легко добавлять новые зависимости или изменять pom для настройки существующих. Таким образом, я надеюсь, что это может быть выполнено с помощью пользовательской аннотации. Например:
@Catcher(action=SomeEnum.SOME_ACTION)
public void doSomething(ServiceObj obj) throws SomeException {
ExternalService.makeThingsHappen(obj);
}
Я бы предположил, что отдельный класс будет обрабатывать исключения, конечно. Дополнительная трудность заключается в том, что мне понадобится ServiceObj, который также передается. Если makeThingsHappen () терпит неудачу, мне может понадобиться obj для выполнения дополнительных действий. Переменная action сообщит классу обработчика, что делать с obj.
можно ли это сделать без серьезных muckery, или я надеялся на что-то, что может не существовать?
2 ответов
это должен быть процесс низкого уровня, и это не означает, что мы не можем иметь то же самое с текущим уровнем, но ему может потребоваться куча кода и немного усложнит систему. Однако мое предложение было бы таким (надеюсь, я правильно понял), сначала определите интерфейс для тех, кто хочет обрабатывать исключения, что-то вроде этого.
interface ExceptionHandler{
void handleException(Throwable t);
}
затем укажите аннотацию для пользователя (API), чтобы отметить, что его методы могут вызывать некоторые исключения.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Catch{
public Class<? extends ExceptionHandler> targetCatchHandler();
public Class<? extends Throwable> targetException() default Exception.class;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface CatchGroup{
public Catch[] catchers();
}
Далее нам нужен интерфейс для запуска вызова метода, который может вызывать исключение, что-то вроде этого.
interface Caller{
void callMethod()throws Throwable;
}
тогда вам нужен парень, который заботится и управляет потоком выполнения и вызывает возможный обработчик исключений
class MethodCaller{
/*
* @param isntance: instance which implemented the Caller interface
*/
public static void callMethod(Caller instance)
throws Exception {
Method m = instance.getClass().getMethod("callMethod");
Annotation as[] = m.getAnnotations();
Catch[] li = null;
for (Annotation a : as) {
if (a.annotationType().equals(CatchGroup.class)) {
li = ((CatchGroup) a).catchers();
}
// for(Catch cx:li){cx.targetException().getName();}
}
try {
instance.callMethod();
} catch (Throwable e) {
Class<?> ec = e.getClass();
if (li == null) {
return;
}
for (Catch cx : li) {
if (cx.targetException().equals(ec)) {
ExceptionHandler h = cx.targetCatchHandler().newInstance();
h.handleException(e);
break;
}
}
}
}
}
и, наконец, давайте приведем пример, он работает очень хорошо для меня, это круто. обработчик исключений.
public class Bar implements ExceptionHandler{//the class who handles the exception
@Override
public void handleException(Throwable t) {
System.out.println("Ta Ta");
System.out.println(t.getMessage());
}
}
и вызывающий метод.
class Foo implements Caller{//the class who calls the method
@Override
@CatchGroup(catchers={
@Catch(targetCatchHandler=Bar.class,targetException=ArithmeticException.class),
@Catch(targetCatchHandler=Bar.class,targetException=NullPointerException.class)})
public void callMethod()throws Throwable {
int a=0,b=10;
System.out.println(b/a);
}
public static void main(String[] args) throws Exception {
Foo foo=new Foo();
MethodCaller.callMethod(foo);
}
}
как вы видите, пользователь должен вызвать методы с помощью callmethod()
способ, вы также опустите Caller
интерфейс, и использовать аннотации, чтобы объявить более одного метода в классе, который он должен кучу дополнительных codez.
Надеюсь, я смогу помочь.
Спасибо за помощь, все. Я заглянул в Spring AOP, но в конечном итоге решил этого не делать. Я закончил с использованием блоков try/catch, но создал обработчик, который вводится в каждый класс и обертывает любые брошенные исключения в моем собственном классе исключений, а затем передает это обработчику в одной строке. Это немного похоже на предложение user2511414 в том, что есть специальный обработчик, но я отказался от аннотаций. У меня есть хорошее количество блоков try/catch, но, по крайней мере, я сохранил большинство логики обработки нет. Краткое изложение моего решения в случае, если другие люди найдут это, что немного запутано, но вы все равно можете получить точку:
public enum DoThisEnum {
DO_THIS,
DO_THAT,
DO_OTHER_THING;
}
public class MyException extends Exception {
private DoThisEnum doThis;
private MyObject dataObj;
//Constructor, overloaded to run super(originalException) or super()
//as well as taking doThis and dataObj as parameters
//Getters, setters, etc
}
public interface IExceptionHandler {
void handleException(MyException exception);
}
затем реализуйте IExceptionHandler с конкретным классом, который принимает MyException, считывает дополнительные данные и выполняет действия на его основе. Тогда каждый блок, который может выдать такое исключение, может быть пойман следующим образом:
...
try {
doSomething(Object data);
} catch (SomeException e) {
handler.handleException(new MyException(e, DoThisEnum.DO_THAT, data));
//Anything else to do, maybe return or return null, rethrow, etc.
}
теперь большая часть nitty-gritty инкапсулируется в обработчик и блоки try / catch минимальны. Обработчик может регистрировать трассировку стека исходного исключения, возможно, делать другие вещи на его основе, а затем выполнять собственные действия на основе перечисления. Возможно, это не идеальное решение, но здесь оно работает достаточно хорошо.