Fundamentos e Fluxo de Execução do Spring MVC

Arquitetura e Componentes Principais

O Spring MVC adota o padrão Model-View-Controller para estruturar aplicações web, separando lógica de negócio, apresentação e controle de tráfego. Os pilares centrais incluem:

  • DispatcherServlet: Controlador frontal central que intercepta todas as requisições HTTP conforme definido no web.xml.
  • HandlerMapping: Responsável por correlacionar URLs às classes anotadas com @Controller.
  • HandlerAdapter: Invoca os métodos de ação correspondentes após a validação de mapeamento.
  • ViewResolver: Traduz nomes lógicos retornados pelos controladores em instâncias reais de View para renderização.

Fluxo de Processamento

  1. Uma solicitação HTTP chega ao servidor e é direcionada ao servlet frontal quando coincide com o padrão configurado.
  2. O contêiner lê o arquivo de configuração do Spring MVC, varre o caminho base em busca de componetnes marcados com @Controller.
  3. O mapa de manipuladores cruza a URL e o método HTTP com as anotações @RequestMapping presentes nas classes.
  4. Caso haja correspondência, o método associado executa a lógica e retorna um identificador de visualização.
  5. O resolvedor concatena prefixos e sufixos configurados para localizar o recurso final, que é enviado ao cliente.

Mapeamento de Requisições com @RequestMapping

A anotação @RequestMapping define a rota acessível aos métodos de controle. Pode ser aplicada em nível de classe (rotas-pai) ou de método (rotas-filha).

Atributos Utilitários

  • value ou path: Define o endpoint esperado.
  • method: Restringe o verbo HTTP permitido (RequestMethod.GET, RequestMethod.POST, etc.). Ausente, permite qualquer método.
  • params: Exige parâmetros específicos na query string ou body para ativar o handler.
  • headers: Filtra requisições baseado em cabeçalhos HTTP obrigatórios.
@RestController
@RequestMapping("/api/financeiro")
public class OperacoesController {

    @RequestMapping(value = "/calcular", method = RequestMethod.GET, params = {"tipo=imposto"})
    public String calcularImposto() {
        return "Processo fiscal concluído.";
    }
}

Coleta de Dados de Entrada

Existem três estratégias oficiais para extrair informações enviadas pelo cliente:

1. Acesso Direto ao Contexto Servlet

@PostMapping("/processar-formulario")
public String coletar(HttpServletRequest requisicao) {
    String termoBusca = requisicao.getParameter("consulta");
    int limiteResultados = Integer.parseInt(requisicao.getParameter("max"));
    
    System.out.printf("Consulta: %s | Máximo: %d%n", termoBusca, limiteResultados);
    return "resultados";
}

2. Parâmetros Nominais no Método

O Spring injeta automaticamente valores da requisição nos argumentos desde que os nomes coincidam. Ideal para cargas leves e ignorar conversões estritas.

@GetMapping("/pesquisar")
public String listarProdutos(String categoria, String ordenacao) {
    System.out.println("Categoria: " + categoria);
    System.out.println("Ordenação: " + ordenacao);
    return "lista-produtos";
}

3. Injeção via Objeto DTO (Recomendado)

Criação de uma entidade simples cujos campos correspondem aos nomes dos parâmetros. O framework realiza o bind automáticamente.

Entidade DTO:

package br.com.devex.model;

public class SolicitacaoServico {
    private Long idSolicitacao;
    private String descricao;
    private Integer prioridade;

    // Getters e Setters omitidos para brevidade
}

Controlador Associado:

@Controller
@RequestMapping("/servicos")
public class GestaoServicosController {

    @PostMapping("/registrar")
    public String registrar(SolicitacaoServico solicitacao) {
        System.out.println("ID: " + solicitacao.getIdSolicitacao());
        System.out.println("Descrição: " + solicitacao.getDescricao());
        System.out.println("Prioridade: " + solicitacao.getPrioridade());
        return "confirmacao";
    }
}

Gestão de Escopo e Transmissão de Dados

Para entregar dados dinâmicos às camadas de apresentação, o Spring oferece mecanismos de escopo pré-definidos pela especificação Jakarta EE:

  • RequestScope: Vínculo exclusivo ao ciclo de vida da solicitação atual.
  • SessionScope: Persiste enformações enquanto a sessão do navegador permanecer ativa.
  • ApplicationScope: Dados compartilhados globalmente entre todos os usuários do contexto.

Exemplos de preenchimento via diferentes abstrações:

// Via Servlet API
@PostMapping("/opcaoA")
public String viaServlet(HttpServletRequest req) {
    req.setAttribute("mensagem", "Operação registrada.");
    return "retorno";
}

// Via ModelAndView
@GetMapping("/opcaoB")
public ModelAndView viaModelAndView() {
    ModelAndView mv = new ModelAndView();
    mv.addObject("usuarioLogado", "admin");
    mv.setViewName("painel-inicial");
    return mv;
}

// Via Map genérico
@PostMapping("/opcaoC")
public String viaMap(Map<string object=""> modelo) {
    modelo.put("dataExibicao", LocalDate.now());
    return "dashboard";
}</string>

Resolução de Visualizações e Navegação

O retorno textual dos handlers é interpretado como nome lógico. Para redirecionar internamente sem passar pelo resolvedor, utiliza-se o prefixo forward:. Já para enviar um novo pedido HTTP ao cliente, emprega-se redirect:. Ambos funcionam tanto com retorno String quanto com ModelAndView.

@GetMapping("/inicio")
public String rotearInicio() {
    return "forward:/pagina-de-aclamaçao";
}

@DeleteMapping("/excluir-recurso")
public String limparRecurso() {
    // Remove recurso e redireciona para lista atualizada
    return "redirect:/consulta-itens";
}

Padrão RESTful e Variáveis de URL

Endpoints declarativos utilizam chaves ({variavel}) no path. O Spring captura esses trechos via @PathVariable, permitindo URIs semânticas e stateless.

@GetMapping("/clientes/{codigo}/detalhes")
public String buscarDetalhes(@PathVariable Long codigo, Model modelo) {
    Cliente cliente = repositorio.buscarPorId(codigo);
    modelo.addAttribute("clienteAtivo", cliente);
    return "perfil-cliente";
}

Conversores e Formatadores Personalizados

Tipos primitivos são tratados nativamente. Para formatos complexos, implementa-se a interface Converter<S, T> ou Formatter<T>.

Diferença chave: Converter aceita qualquer tipo fonte, enquanto Formatter exige String como entrada.

public class TransformadorData implements Converter<String, Date> {
    private static final String PADRAO = "dd/MM/yyyy";

    @Override
    public Date convert(String origem) {
        SimpleDateFormat parser = new SimpleDateFormat(PADRAO);
        try {
            return parser.parse(origem);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Formato inválido. Use dd/MM/yyyy");
        }
    }
}
// Registro obrigatório no context.xml ou classe de configuração Java

Para formatação nativa, as anotações @DateTimeFormat e @NumberFormat automatizam a leitura e exibição:

public class RelatórioFinanceiro {
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDateTime dataEmissao;

    @NumberFormat(style = Style.CURRENCY)
    private BigDecimal saldoDisponivel;
}

Integração Assíncrona e Troca de JSON

Controladores expostos como APIs retornam objetos serializados automaticamente quando marcados com @ResponseBody. O receptor utiliza bibliotecas JS para montar payloads ou consumir respostas.

@RestController
@RequestMapping("/catalogo")
public class CatalogoController {

    @GetMapping("/{id}")
    public Produto visualizar(@PathVariable String id) {
        return servico.consultar(id);
    }

    @PostMapping(consumes = "application/json")
    public Produto criar(@RequestBody Produto payload) {
        servico.registrar(payload);
        return payload;
    }
}

Exemplo de consumo front-end com jQuery:

<table id="tabela-catalogo"></table>
<div id="modal-registro" style="display:none;">
    <form id="form-novo">
        <input type="text" name="nomeProduto" placeholder="Nome">
        <button type="submit">Salvar</button>
    </form>
</div>

<script>
$(document).ready(function() {
    $.ajax({
        url: "/catalogo/buscar-lista",
        type: "GET",
        dataType: "json",
        success: function(produtos) {
            let html = "";
            produtos.forEach(item => {
                html += "<tr><td>" + item.codigo + "</td>" +
                        "<td>" + item.nomeProduto + "</td></tr>";
            });
            $("#tabela-catalogo").append(html);
        }
    });

    $("#form-novo").on("submit", function(e) {
        e.preventDefault();
        const dados = {
            nomeProduto: $("input[name='nomeProduto']").val()
        };
        
        $.ajax({
            url: "/catalogo",
            type: "POST",
            contentType: "application/json",
            data: JSON.stringify(dados),
            success: function() {
                $("#modal-registro").hide();
                location.reload();
            }
        });
    });
});
</script>

Tags: spring-mvc requestmapping json-rest viewresolver converter-java

Publicado em 8-26 20:07