Propagação de Contexto de Solicitação em Subthreads com Spring MVC

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.

Tags: spring-mvc ThreadLocal requestattributes java async-executor

Publicado em 7-17 07:13