Chcę utworzyć program obsługi wyjątków, który przechwyci wszystkie kontrolery w moim projekcie. Czy to możliwe? Wygląda na to, że muszę umieścić metodę obsługi w każdym kontrolerze. Dzięki za pomoc. Mam kontroler sprężyny, który wysyła odpowiedź Json. Więc jeśli zdarzy się wyjątek, chcę wysłać odpowiedź o błędzie, którą można kontrolować z jednego miejsca.
3 odpowiedzi
(Znalazłem sposób na zaimplementowanie go w Spring 3.1, jest to opisane w drugiej części tej odpowiedzi)
Zobacz rozdział 16.11 Obsługa wyjątki Spring Reference
Jest więcej sposobów niż użycie @ExceptionHandler
(zobacz odpowiedź goukiego a>)
- Możesz zaimplementować HandlerExceptionResolver< /a> (użyj serwletu, a nie pakietu portletu) - to jest jakiś globalny @ExceptionHandler
Jeśli nie masz określonej logiki dla wyjątku, a jedynie konkretny widok, możesz użyć SimpleMappingExceptionResolver, który jest przynajmniej implementacją
HandlerExceptionResolver
, w której można określić wzorzec nazwy wyjątku i widok (jsp) który jest wyświetlany, gdy zostanie zgłoszony wyjątek. Na przykład:<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">dataAccessFailure</prop> <prop key=".TypeMismatchException">resourceNotFound</prop> <prop key=".AccessDeniedException">accessDenied</prop> </props> </property> </bean>
W Spring 3.2+ można opisać klasę za pomocą @ControllerAdvice
, wszystkie @ExceptionHandler
metody w tej klasie działają globalnie.
W Wiosnie 3.1 nie ma @ControllerAdvice
. Ale przy odrobinie hacka można mieć podobną funkcję.
Kluczem jest zrozumienie sposobu działania @ExceptionHandler
. W Spring 3.1 istnieje klasa ExceptionHandlerExceptionResolver
. Ta klasa implementuje (z pomocą swoich nadklas) interfejs HandlerExceptionResolver
i odpowiada za wywoływanie metod @ExceptionHandler
.
Interfejs HandlerExceptionResolver
ma tylko jedną metodę:
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);`.
Gdy żądanie było obsługiwane przez metodę kontrolera Spring 3.x, ta metoda (reprezentowana przez org.springframework.web.method.HandlerMethod
) jest parametrem handler
.
ExceptionHandlerExceptionResolver
używa handler
(HandlerMethod
), aby uzyskać klasę Controller i przeskanować ją w poszukiwaniu metod oznaczonych @ExceptionHandler
. Jeśli jedna z tych metod pasuje do wyjątku (ex
), wówczas te metody są wywoływane w celu obsługi wyjątku. (w przeciwnym razie null
zostanie zwrócony, aby zasygnalizować, że ten mechanizm rozpoznawania wyjątków nie czuje się odpowiedzialny).
Pierwszym pomysłem byłoby zaimplementowanie własnego HandlerExceptionResolver
, które zachowuje się jak ExceptionHandlerExceptionResolver
, ale zamiast szukać @ExceptionHandler
w klasie kontrolera, powinno szukać ich w jednym specjalnym ziarnku. Wadą byłoby to, że trzeba (skopiować (lub podklasę ExceptionHandlerExceptionResolver
) i trzeba) skonfigurować wszystkie ładne konwertery komunikatów, programy rozpoznawania argumentów i obsługi zwracanych wartości (konfiguracja rzeczywistej i tylko ExceptionHandlerExceptionResolver
odbywa się automatycznie przez sprężynę). Wpadłem więc na inny pomysł:
Zaimplementuj prosty HandlerExceptionResolver
, który "przesyła" wyjątek do (już skonfigurowanego) ExceptionHandlerExceptionResolver
, ALE ze zmodyfikowanym handler
, który wskazuje na ziarno zawierające globalne procedury obsługi wyjątków (nazywam je globalne, ponieważ wykonują pracę dla wszystkich kontrolerów).
A oto implementacja: GlobalMethodHandlerExeptionResolver
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
public class GlobalMethodHandlerExeptionResolver
implements HandlerExceptionResolver, Ordered {
@Override
public int getOrder() {
return -1; //
}
private ExceptionHandlerExceptionResolver realExceptionResolver;
private List<GlobalMethodExceptionResolverContainer> containers;
@Autowired
public GlobalMethodHandlerExeptionResolver(
ExceptionHandlerExceptionResolver realExceptionResolver,
List<GlobalMethodExceptionResolverContainer> containers) {
this.realExceptionResolver = realExceptionResolver;
this.containers = containers;
}
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
for (GlobalMethodExceptionResolverContainer container : this.containers) {
ModelAndView result = this.realExceptionResolver.resolveException(
request,
response,
handlerMethodPointingGlobalExceptionContainerBean(container),
ex);
if (result != null)
return result;
}
// we feel not responsible
return null;
}
protected HandlerMethod handlerMethodPointingGlobalExceptionContainerBean(
GlobalMethodExceptionResolverContainer container) {
try {
return new HandlerMethod(container,
GlobalMethodExceptionResolverContainer.class.
getMethod("fakeHanderMethod"));
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
Globalny Handler musi zaimplementować ten interfejs (w celu znalezienia i zaimplementowania fakeHanderMethod
używanego dla handler
public interface GlobalMethodExceptionResolverContainer {
void fakeHanderMethod();
}
I przykład dla globalnego Handlera:
@Component
public class JsonGlobalExceptionResolver
implements GlobalMethodExceptionResolverContainer {
@Override
public void fakeHanderMethod() {
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorDto handleMethodArgumentNotValidException(
MethodArgumentNotValidException validationException,
Locale locale) {
...
/* map validationException.getBindingResult().getFieldErrors()
* to ValidationErrorDto (custom class) */
return validationErrorDto;
}
}
BTW: Nie musisz rejestrować GlobalMethodHandlerExeptionResolver
, ponieważ Spring automatycznie rejestruje wszystkie ziarna, które implementują HandlerExceptionResolver
dla rozwiązywania wyjątków. Więc wystarczy prosty <bean class="GlobalMethodHandlerExeptionResolver"/>
.
@ExceptionHandler
metody odpowiedzialne za kontrolery. - zobacz moją rozszerzoną odpowiedź.
Od wersji Spring 3.2 możesz używać @ControllerAdvice adnotacja. Możesz zadeklarować @ExceptionHandler w ramach klasy @ControllerAdvice w takim przypadku obsługuje wyjątki od metod @RequestMapping ze wszystkich kontrolerów.
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(value=IOException.class)
public @ResponseBody String iOExceptionHandler(Exception ex){
//
//
}
// other exception handler methods
// ...
}
Klasa abstrakcyjna, w której zdefiniujesz obsługę wyjątków. A następnie spraw, aby twoje kontrolery go odziedziczyły.
Podobne pytania
Powiązane pytania
Nowe pytania
spring
Spring Framework to framework open source do tworzenia aplikacji na platformie Java. U jej podstaw leży bogate wsparcie dla architektur opartych na komponentach, a obecnie ma ponad dwadzieścia wysoce zintegrowanych modułów.