2013-11-14 8 views
9

W mojej służbie Grails mam kod jak poniżej:Wyjątek wyrzucony z usługi nie przyłapania w kontrolerze

def createCharge(chargeParams) { 
    try { 
    def charge = Charge.create(chargeParams) 
    } catch (CardException e) { 
    throw e 
    } 
} 

Od mojego kontrolera I wykonaj następujące czynności:

try { 
    service.createCharge(chargeParams) 
} catch(CardException e) { 

} 

Jednak mój regulator jest nie łapie ponownego rzucania wyjątku CardException. Gdybym owinąć CardException w RuntimeException poprzez:

throw new RuntimeException(e) 

i/lub usunąć sygnaturę z połowu po prostu catch (e) bez wpisywania go, to działa, ale tracę trochę informacji z wyjątkiem, jak wiadomości .

Uwaga: CardException jest wyjątkiem, a nie wyjątkiem RuntimeException. Nie jestem pewien, czy to ma znaczenie.

Odpowiedz

11

W przeciwieństwie do języka Java, nie trzeba zadeklarować (zaznaczonych) wyjątków, które są generowane przez metodę Groovy, ponieważ wszelkie niezadeklarowane sprawdzane wyjątki są zawijane w UndeclaredThrowableException. Więc tak:

def createCharge(chargeParams) { 
    try { 
    def charge = Charge.create(chargeParams) 
    } catch (CardException e) { 
    throw e 
    } 
} 

jest faktycznie taki sam jak:

def createCharge(chargeParams) throws UndeclaredThrowableException { 
    try { 
    def charge = Charge.create(chargeParams) 
    } catch (CardException e) { 
    throw new UndeclaredThrowableException(e) 
    } 
} 

wyjątek rzucony przez wyżej, oczywiście nie byłby złapany przez:

try { 
    service.createCharge(chargeParams) 
} catch(CardException e) { 

} 

Jednak zostanie on złapany :

try { 
    service.createCharge(chargeParams) 
} catch(e) { 

} 

Ponieważ jest po prostu skrótem dla:

try { 
    service.createCharge(chargeParams) 
} catch(Exception e) { 

} 
+0

Cóż, potrzebuję sprawdzonych wyjątków w moim przypadku, ponieważ jest kilka aspektów, którymi muszę się zająć od API Stripe. Skończyło się na innej trasie, ponieważ muszę również rzucić wyjątek RuntimeException, aby moje transakcje zostały poprawnie wycofane. Zaznaczam twoją odpowiedź jako poprawną, ponieważ jest to właściwa odpowiedź na moje pytanie. Dzięki. – Gregg

+2

Nie ma za co. BTW, możesz wykonać wycofanie transakcji dla wszystkich wyjątków (zaznaczone i niezaznaczone), dodając następującą adnotację do usług Grails '@Transactional (rollbackFor = Throwable)' –

+0

Dzięki za napiwek! Nie wiedział tego. – Gregg

8

przeciwieństwie do Javy, nie trzeba zadeklarować (zaznaczone) wyjątków, które są generowane przez metodę Groovy, bo jakieś niewypowiedziane sprawdzane wyjątki są owinięte w UndeclaredThrowableException.

Wydaje się sugerować, że Groovy okłady Wyjątki sprawdzane przez UndeclaredThrowableException, to nie jest przypadek. Jeśli usługa Grails zgłasza niezaznaczony wyjątek, wyjątek zostaje ostatecznie uszkodzony przez wyjątek UndeclaredThrowableException, ale jest to mechanizm java.lang.reflection i wystąpi tylko wtedy, gdy zaangażowana jest klasa proxy.

Tak się składa, ponieważ dotyczy to usługi Grails. Nie mam pewności, ile klas proxy jest dokładnie zaangażowanych, ponieważ istnieje co najmniej jeden: klasa, która obsługuje transakcję (wiosną).

Klasa zawinie dowolną metodę w usłudze za pomocą transakcji i wycofa transakcję po wystąpieniu wyjątku RuntimeException. Zarządzanie transakcjami wiosennymi domyślnie nie wycofuje się w przypadku sprawdzonego wyjątku.

Java

to sens w zwykły stary java, ponieważ deweloper będzie zobaczyć wyjątek w kodzie aplikacji i zostanie ostrzeżony, aby coś z tym zrobić. Jeśli programista jest sprytny, zajmie się wszystkimi wyjątkami w zakresie transakcji.Jeśli nie wycofa transakcji, zasadniczo mówi: "W tej sytuacji integralność danych dostarczona przez transakcję nie jest dla mnie ważna. Będę odzyskać od tego błędu w jakiś inny sposób”

Groovy

To nie ma sensu w świecie, ponieważ Groovy Groovy kompilator nie wymusza czynienia z wyjątkami. W efekcie traktuje Wyjątki dokładnie tak samo, jak Wyjątki RuntimeException.

Istnieje jednak zastrzeżenie: mechanizm odbicia widzi wyjątek zgłoszony przez serwer proxy, który nie znajduje się w metodzie podpisu oryginalnej usługi. Jest to możliwe, ponieważ:

  1. Groovy nie wymusza obsługę Wyjątki
  2. Sposób proxy zawsze można rzucać żadnych Throwable (sprawdź InvocationHandler JavaDoc)

Ponieważ mechanizm stosowany jest odbicie od Java musi być zgodny z regułami Java. Więc będzie musiał zawinąć wyjątek w RuntimeException, .. w tym przypadku wyjątek UndeclaredThrowableException.

Grails

Teraz robi się naprawdę trudne, ponieważ jeśli zadzwonić do metody usługi od kontrolera i występuje wyjątek. Zobaczysz dymek RuntimeException (z powodu ukrytego mechanizmu), ale twoja transakcja nie zostanie wycofana (z powodu jakiegoś ukrytego mechanizmu).

Takie zachowanie jest bardzo niebezpiecznejako deweloper naprawdę musi pamiętać, aby prawidłowo radzić sobie z wszelkimi wyjątkami (które kompilator nie pomogą) lub deweloper musi się upewnić, że wszelkie usługi jest odpowiednio poinstruowani z @Transactional (rollbackFor = Throwable).

Jest to problem z konstrukcją, który, jak sądzę, twórcy Grails przeoczyli, kiedy po raz pierwszy to zaprojektowali. Ale myślę, że domyślne zachowanie jest tak złe i tak niebezpieczne, że to naprawdę powinno zostać zmienione.

2

Myślę, że proste rozwiązanie problemu polega na dodaniu do metody serwisowej instrukcji throws CardException, w związku z czym wyjątek nie będzie już zapakowany w UndeclaredThrowableException, a kontroler wykryje odpowiedni typ wyjątku.

0

Po prostu uchwyć wyjątek UndeclaredThrowableException, odbierz wiadomość, a następnie w razie potrzeby prześlij ponownie.

catch (UndeclaredThrowableException e) { 
    def s = e.getUndeclaredThrowable().getMessage() 
    // do something with the message 
    throw e 
} 

Powyższy kod fragment po prostu złapać wyjątek już wyraźnie rzucony w kodzie (np CardException). Na przykład wyjątek NullPointerException nie zostanie przechwycony i nie będzie bańki.

Powiązane problemy