RequestContextHolder.getRequestAttributes()를 사용해 HTTP 요청 정보를 가져올 수 있다.
RequestAttributes가 ServletRequestAttributes 인스턴스인 경우만 HTTP 요청을 다룰 수 있다.
비동기 작업(예: @Async 메서드, CompletableFuture 등)이나 스케줄러에서 호출되면 ServletRequestAttributes를 찾을 수 없다.
이 경우 예외를 던지거나 대체값을 반환하는 방법을 사용한다.
IllegalStateException
요청 컨텍스트가 없으면 IllegalStateException을 던진다.
private HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
throw new IllegalStateException("No current HTTP request available. This can happen outside of an HTTP request context (e.g., async tasks or scheduled tasks).");
}
커스텀 예외
요청 컨텍스트가 없을 때 커스텀 예외를 던져 더 명확한 메시지를 전달한다.
public class RequestContextNotFoundException extends RuntimeException {
public RequestContextNotFoundException(String message) {
super(message);
}
}
private HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
throw new RequestContextNotFoundException("No HTTP request context available. This is likely caused by non-HTTP or background task execution.");
}
비동기 작업에서 요청 컨텍스트를 전파하려면 TaskDecorator를 사용한다.
import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
public class ContextCopyingDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.getRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class AsyncConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncExecutor-");
executor.setTaskDecorator(new ContextCopyingDecorator());
return executor;
}
}
AuditorAware를 사용하려면 반드시 Spring Bean으로 등록해야 한다.
@Component, @Bean 등을 사용해 AuditorAwareImpl을 Spring 컨텍스트에 등록한다.
@Component
public class AuditorAwareImpl implements AuditorAware<Long> {
@Override
public Optional<Long> getCurrentAuditor() {
Span currentSpan = Span.current();
if (!currentSpan.getSpanContext().isValid()) {
return Optional.empty();
}
String userId = currentSpan.getAttribute("user.id");
if (userId != null) {
return Optional.of(Long.valueOf(userId));
}
return Optional.empty();
}
}
@Component 대신, @Bean으로 AuditorAware를 등록할 수 있다.
@Configuration
public class AuditorAwareConfig {
@Bean
public AuditorAware<Long> auditorAware() {
return new AuditorAwareImpl();
}
}