Потеря объекта HttpServletRequest при работе с исключением в AOP для метода @Async

Я последовал за это и создал Executor, Callable и ExecutorConfig точно так, как описано в ответе. Теперь я начал получать объект HttpServletRequest в коде AOP, но объект ничего не содержит. например, request.getRequestURI() дает NULL. В моем коде AOP мне просто нужно прочитать объекты Throwable и HttpServletRequest, чтобы сохранить информацию об ошибках и некоторые важные заголовки запросов вместе с URI в таблице.

Вот мой код АОП -

@Aspect
@Component
public class ErrorAspect {

    private static final String EXCEPTION_EXECUTION_PATH = "execution(* com.myproject.*.service.impl.*.*(..))";

    @Autowired
    private ErrorHelper         errorHelper;

    @Pointcut( EXCEPTION_EXECUTION_PATH)
    public void atExecutionExcpetion() {
    }

    @AfterThrowing( value = "atExecutionExcpetion()", throwing = "error")
    public void storeErrorAfterThrowing( Throwable error) {
        errorHelper.saveError(error);
    }
}

И метод saveError() в ErrorHelper -

public void saveError( Throwable error) {
        HttpServletRequest request = null;
        if (RequestContextHolder.getRequestAttributes() != null) {
            request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        }
        Error error = prepareError(request, error);
        CompletableFuture.runAsync(() -> insertError(error));
    }  

private Error prepareError( HttpServletRequest request, Throwable error) {
    Error error = new Error();
    if (request == null) {
        String process = Constants.AUTO_JOB + LocalDateTime.now(ZoneId.of(PST_ZONE_ID)).toString().replaceAll("-", "");
        error.setProcessType(Constants.AUTO_JOB);
        error.setApplicationId(process);
        error.setSessionId(process);
        error.setUri(NA);
    } else {
        error.setProcessType(request.getHeader(Constants.PROCESS_ID));
        error.setApplicationId(request.getHeader(Constants.APPLICATION_ID));
        error.setSessionId(request.getHeader(Constants.SESSION_ID));
        error.setUri(request.getRequestURI());
    }
    error.setEventDateTime(Instant.now());
    error.setErrorType(getErrorType(error));
    error.setErrorMessage(getErrorMessage(error));
    return error;
}

Это отлично работает с синхронными вызовами. Но для вызовов @Async в объекте запроса нет информации о заголовке/URI.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
919
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Создал декоратор и скопировал требуемый атрибут запроса в MDC. Вот код декоратора -

public class ContextAwareExecutorDecorator implements Executor, TaskExecutor {

    private final Executor executor;

    public ContextAwareExecutorDecorator( Executor executor) {
        this.executor = executor;
    }

    @Override
    public void execute( Runnable command) {
        Runnable ctxAwareCommand = decorateContextAware(command);
        executor.execute(ctxAwareCommand);
    }

    private Runnable decorateContextAware( Runnable command) {
        RequestAttributes originalRequestContext = RequestContextHolder.currentRequestAttributes();

        if (originalRequestContext != null) {
            HttpServletRequest request = ((ServletRequestAttributes) originalRequestContext).getRequest();
            copyRequestToMDC(request);
        }

        final Map<String, String> originalContextCopy = MDC.getCopyOfContextMap();
        return () -> {
            try {
                if (originalRequestContext != null) {
                    RequestContextHolder.setRequestAttributes(originalRequestContext);
                }
                MDC.setContextMap(originalContextCopy);
                command.run();
            } finally {
                MDC.clear();
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }

    private void copyRequestToMDC( HttpServletRequest request) {
        if (request != null) {
            MDC.put("requestURI", request.getRequestURI());
            // Set other required attributes
        }
    }
}

Вот конфиг исполнителя -

@Configuration
public class ExecutorConfig extends AsyncConfigurerSupport {

    @Override
    @Bean( "asyncTaskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("contextAwareExecutor-");
        executor.initialize();
        return new ContextAwareExecutorDecorator(executor);
    }

    @Override
    @Bean
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}

Теперь в коде АОП я могу получать атрибуты из MDC.

error.setUri(MDC.get("requestURI"));

У меня такая же проблема, как и здесь, stackoverflow.com/questions/63116739/…, не могли бы вы помочь мне решить эту проблему? что такое MDC в ваших кодах?

Rasool Ghafari 29.07.2020 07:06

Другие вопросы по теме