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
- Uma solicitação HTTP chega ao servidor e é direcionada ao servlet frontal quando coincide com o padrão configurado.
- O contêiner lê o arquivo de configuração do Spring MVC, varre o caminho base em busca de componetnes marcados com
@Controller. - O mapa de manipuladores cruza a URL e o método HTTP com as anotações
@RequestMappingpresentes nas classes. - Caso haja correspondência, o método associado executa a lógica e retorna um identificador de visualização.
- 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
valueoupath: 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>