我关注了this并完全按照答案中的描述创建了 Executor、Callable 和 ExecutorConfig。现在我开始在 AOP 代码中获取 HttpServletRequest 对象,但该对象不包含任何内容。例如 request.getRequestURI() 给出 NULL。 在我的 AOP 代码中,我只需要读取 Throwable 和 HttpServletRequest 对象来存储错误信息和一些重要的请求 header 以及表中的 URI。
这是我的 AOP 代码 -
@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);
}
}
ErrorHelper 中的 saveError() 方法是 -
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 调用,请求对象中没有 header/uri 信息。
最佳答案
创建装饰器并将所需的请求属性复制到 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();
}
}
现在,在 AOP 代码中,我可以从 MDC 检索属性。
error.setUri(MDC.get("requestURI"));
关于java - 在 AOP 中为 @Async 方法处理异常时丢失 HttpServletRequest 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56677077/