Добавить обработчик исключений Spring программно, без аннотации @ExceptionHandler (или любой другой)? - PullRequest
1 голос
/ 25 марта 2020

Весной я бы хотел добавить обработчик исключений программно, не полагаясь на то, что система будет сканировать методы, помеченные @ExceptionHandler. Возможно ли это?

В настоящее время я полагаюсь на систему для сканирования следующего метода:

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<String> handle(
        final HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException,
        final HttpServletRequest httpServletRequest,
        final HttpServletResponse httpServletResponse)
{
    return ResponseEntity
            .status(METHOD_NOT_ALLOWED)
            .body(format(
                    "Unsupported method '%s' for URI '%s'; expected method among: %s.",
                    httpRequestMethodNotSupportedException.getMethod(),
                    httpServletRequest.getRequestURI(),
                    join(", ", asList(httpRequestMethodNotSupportedException.getSupportedMethods())
                            .stream()
                            .map(object -> "'" + object + "'")
                            .collect(toList()))));
}

И он работает, как и ожидалось, обрабатывает HttpRequestMethodNotSupportedException и сериализует ответ как и ожидалось:

Unsupported method 'DELETE' for URI '/example'; expected method among: 'GET', 'POST'.

Однако я не знаю, как, скажем, использовать конфигурацию Java, чтобы указать, что он должен использовать определенный класс или конкретный экземпляр для обработки указанных c исключения.

У меня была похожая проблема с программным добавлением реализаций ResponseBodyAdvice, и есть способ сделать это - см. здесь:

@Configuration
@ComponentScan(basePackageClasses = ExceptionHandler.class)
public class ConfigurationExample
        extends WebMvcConfigurationSupport
{
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
            @Qualifier(value = "mvcContentNegotiationManager") final ContentNegotiationManager contentNegotiationManager,
            @Qualifier(value = "mvcConversionService") final FormattingConversionService conversionService,
            @Qualifier(value = "mvcValidator") final Validator validator)
    {
        final RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);

        requestMappingHandlerAdapter.setResponseBodyAdvice(Arrays.asList(
                new ResponseBodyAdviceExample()));

        return requestMappingHandlerAdapter;
    }
}

Существует ли аналогичный метод для добавления пользовательские обработчики исключений - или любой другой?

Для фона моя цель - создать библиотеку обработчиков исключений, которую я могу использовать в разных проектах.

1 Ответ

0 голосов
/ 26 марта 2020

Я не знаю, есть ли идиоматический c способ сделать это, но я нашел способ программно добавлять обработчики исключений.

(я подожду несколько недель, чтобы пометить это как принимаются, если кто-то хочет предложить альтернативу.)

Код здесь:

https://gist.github.com/drewctaylor/f838eb8304d7c526872ed29934a1e830

В конечном счете, я подкласс ExceptionHandlerExceptionResolver. Под капотом ExceptionHandlerExceptionResolver по существу отображается от java.lang.Class<java.lang.Exception> до java.lang.reflect.Method; если происходит исключение данного типа, система возвращает данный метод.

Эта реализация принимает список сопоставлений от java.lang.Class<java.lang.Exception> до java.lang.reflect.Method. Если возникает исключение типа в этом списке, он возвращает связанный метод; в противном случае относится к суперклассу.

Код для реализации, ExceptionHandlerExceptionResolverFromList:

public class ExceptionHandlerExceptionResolverFromList
        extends ExceptionHandlerExceptionResolver
{
    private final Map<Class<?>, ExceptionHandlerWithExceptionClassAndMethod<?>> map;

    public ExceptionHandlerExceptionResolverFromList(
            final List<ExceptionHandlerWithExceptionClassAndMethod<?>> list)
    {
        requireNonNull(list);

        map = list.stream().collect(toMap(ExceptionHandlerWithExceptionClassAndMethod::getExceptionClass, Function.identity()));
    }

    @Nullable
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
            @Nullable final HandlerMethod handlerMethod,
            final Exception exception)
    {
        requireNonNull(exception);

        return map.containsKey(exception.getClass()) ?
                new ServletInvocableHandlerMethod(map.get(exception.getClass()), map.get(exception.getClass()).getMethod()) :
                super.getExceptionHandlerMethod(handlerMethod, exception);
    }
}

Отображение определяется реализацией интерфейса ExceptionHandlerWithExceptionClassAndMethod:

public interface ExceptionHandlerWithExceptionClassAndMethod<E extends Exception>
{
    Class<E> getExceptionClass();

    Method getMethod();
}

Специфической реализацией c является ExceptionHandlerForHttpRequestMethodNotSupportedException, аналогично примеру в вопросе:

public class ExceptionHandlerForHttpRequestMethodNotSupportedException
        implements ExceptionHandlerWithExceptionClassAndMethod<HttpRequestMethodNotSupportedException>
{
    @Override
    public Class<HttpRequestMethodNotSupportedException> getExceptionClass()
    {
        return HttpRequestMethodNotSupportedException.class;
    }

    @Override
    public Method getMethod()
    {
        try
        {
            return getClass().getDeclaredMethod(
                    "handle",
                    HttpRequestMethodNotSupportedException.class,
                    HttpServletRequest.class,
                    HttpServletResponse.class);
        }
        catch (Exception exception)
        {
            throw new RuntimeException(exception);
        }
    }

    public ResponseEntity<String> handle(
            final HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException,
            final HttpServletRequest httpServletRequest,
            final HttpServletResponse httpServletResponse)
    {
        return ResponseEntity.status(METHOD_NOT_ALLOWED).body(
                format(
                        "Unsupported method '%s' for URI '%s'; expected method among: %s.",
                        httpRequestMethodNotSupportedException.getMethod(),
                        httpServletRequest.getRequestURI(),
                        join(", ", asList(httpRequestMethodNotSupportedException.getSupportedMethods()).stream().map(object -> "'" + object + "'").collect(toList()))));
    }
}

Обратите внимание, что вышеприведенное объединяет отображение с реализацией обработчика, который не является строго необходимо, но в настоящее время кажется уместным.

Чтобы добавить обработчик в приложение, расширьте Configuration с WebMvcConfigurationSupport и переопределите createExceptionHandlerExceptionResolver для возврата экземпляра ExceptionHandlerExceptionResolverFromList:

@org.springframework.context.annotation.Configuration
public class Configuration
        extends WebMvcConfigurationSupport
{
    protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver()
    {
        return new ExceptionHandlerExceptionResolverFromList(asList(
                new ExceptionHandlerForHttpRequestMethodNotSupportedException()));
    }
}

Приложение теперь будет использовать ExceptionHandlerForHttpRequestMethodNotSupportedException для обработки HttpRequestMethodNotSupportedException; для других исключений он будет использовать любые другие определенные обработчики.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...