No desenvolvimento com Spring MVC, acessar atributos da solicitação HTTP em subthreads requer atenção especial, pois o RequestContextHolder armazena dados no ThreadLocal do thread atual. Subthreads não herdam automaticamente esses valores, resultendo em null ao tentar obtê-los.
Exemplo que demonstra o problema:
@GetMapping("/endpoint-assincrono")
public String executarEndpoint() {
// No thread primário, os atributos estão disponíveis
RequestAttributes dadosPrincipal = RequestContextHolder.getRequestAttributes();
System.out.println("Thread primário: " + dadosPrincipal); // valor válido
// Criação de subthread
new Thread(() -> {
// Na subthread, os atributos não estão presentes
RequestAttributes dadosSub = RequestContextHolder.getRequestAttributes();
System.out.println("Subthread: " + dadosSub); // null
}).start();
return "processado";
}
Isso ocorre porque o ThreadLocal isola dados por thread, e o Spring não propaga o conetxto da solicitação para threads filhos. Para resolver isso, existem técnicas específicas.
Abordagem 1: Transmissão Explícita de Dados
Capture os atributos no thread principal e passe-os manualmente para a subthread:
public void processarAssincrono() {
// Capturar atributos no thread principal
RequestAttributes contextoAtual = RequestContextHolder.getRequestAttributes();
// Iniciar subthread com transmissão
new Thread(() -> {
try {
// Configurar atributos na subthread
RequestContextHolder.setRequestAttributes(contextoAtual);
// Usar o contexto para lógica de negócio
HttpServletRequest requisicao = ((ServletRequestAttributes) contextoAtual)
.getRequest();
String tokenAutenticacao = requisicao.getHeader("X-Auth-Token");
// Implementação da lógica...
} finally {
// Limpar para evitar vazamentos de memória
RequestContextHolder.resetRequestAttributes();
}
}).start();
}
Abordagem 2: Utilização de CompletableFuture com Configuração Adequada
Com anotações do Spring e configuração correta, o contexto pode ser propagado automaticamente:
@Async
public CompletableFuture<string> tarefaAsync() {
// Sob configuração adequada, o contexto é transmitido
RequestAttributes dadosContexto = RequestContextHolder.getRequestAttributes();
System.out.println("Em tarefa assíncrona: " + dadosContexto);
return CompletableFuture.completedFuture("sucesso");
}</string>
Abordagem 3: Customização do Executor de Tarefas
Configure um TaskExecutor que permita herança de atributos da solicitação:
@Configuration
@EnableAsync
public class ConfigAsync implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("Assync-");
// Habilita propagação de contexto
executor.setTaskDecorator(new DecoradorContextoRequisicao());
executor.initialize();
return executor;
}
}
class DecoradorContextoRequisicao implements TaskDecorator {
@Override
public Runnable decorate(Runnable acao) {
RequestAttributes atributos = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(atributos);
acao.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
Essas soluções asseguram que informações como cabeçalhos de autenticação ou dados de sessão estejam acessíveis em subthreads, mantendo a integridade do contexto da aplicação.