异步记录日志获取不到HttpServletRequest中的URI@NPE异常
现象
侵入业务代码中记录审计日志时,需要根据HttpServletRequest请求获取需要的URI,但是获取为null
@Service
public class LogService {
@Autowired
private LogMapper logMapper;
@Async
@Transactional
public void saveOptLog(String user, AuditLogContext log) {
logMapper.insert(log);
}
}
原因
记录日志是异步的,这个时候的request已经不是用户发起请求的request了,而是新建线程的request
解决方案:
方案1、request作为参数进行传递
public class AuditLogContext {
public static AuditLogContext init(HttpServletRequest request, String operationContent) {
AuditLogContext auditLogContext = new AuditLogContext();
auditLogContext.setOperationContent(operationContent);
auditLogContext.setRequestURI(request.getRequestURI());
return auditLogContext;
}
}
方案 2、需要实现spring的一个接口TaskDecorator
// 线程池配置
@Configuration
public class AsyncConfig {
private static final int MAX_POOL_SIZE = 50;
private static final int CORE_POOL_SIZE = 20;
@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
final ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setThreadNamePrefix("async-task-thread-pool-");
taskExecutor.setTaskDecorator(new ContextDecorator());
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize();
return taskExecutor;
}
}
public class ContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}