Neste artigo, exploramos três aplicações web construídas com Spring MVC, demonstrando a interação entre o cliente e o servidor e a organização do código em camadas.
Definição da Interface entre Cliente e Servidor
Definir uma interface de comunicação (API) clara é fundamental no desenvolvimento web moderno. Ela descreve como o cliente (frontend) deve fazer requisições ao servidor (backend) e quais respostas esperar. Em ambientes com times de desenvolvimento separados, essa especificação é documentada em um documento de API, que serve como manual de operação e normalmente é fornecido pelo time de backend.
1. Calculadora Simples
Análise do Requisito
Desenvolver uma calculadora de adição que receba dois números inteiros do cliente e retorne o resultado da soma.
Definição da API
- Caminho:
/calculadora/soma - Método: POST
- Parâmetros:
valorA,valorB(no corpo da requisição como formulário) - Resposta (Content-Type: text/html): Uma página HTML simples com o cálculo.
Código do Frontend (calc.html)
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Calculadora Web</title>
</head>
<body>
<h2>Calculadora de Soma</h2>
<form action="/calculadora/soma" method="post">
Primeiro Número: <input name="valorA" type="number" required><br><br>
Segundo Número: <input name="valorB" type="number" required><br><br>
<input type="submit" value="Calcular Soma">
</form>
</body>
</html>
Código do Backend
package com.exemplo.demo.controlador;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/calculadora")
public class ControladorCalculadora {
@PostMapping("/soma")
public String calcularSoma(@RequestParam int valorA, @RequestParam int valorB) {
int resultado = valorA + valorB;
return "<h3>O resultado de " + valorA + " + " + valorB + " é: <strong>" + resultado + "</strong></h3>";
}
}
Ao iniciar a aplicação, a calculadora pode ser acessada em http://localhost:8080/calc.html.
2. Sistema de Login de Usuário
Definição do Requisito
Um sistema onde o usuário insere credenciais. O backend as valida; se corretas, armazena o estado da sessão e redireciona para uma página inicial que exibe o nome do usuário logado.
Definição das APIs
Endpoint de Autenticação
- Caminho:
/api/autenticacao - Método: POST
- Corpo:
{"login": "usuario", "senha": "senha123"} - Resposta:
trueoufalse(boolean).
Endpoint para Obter Usuário Logado
- Caminho:
/api/usuario-logado - Método: GET
- Resposta: O login do usuário autenticado ou uma string vazia.
Código do Backend
package com.exemplo.demo.controlador;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.*;
import java.util.Objects;
@RestController
@RequestMapping("/api")
public class ControladorAutenticacao {
// Simulação de banco de dados
private static final String USUARIO_VALIDO = "admin";
private static final String SENHA_VALIDA = "admin123";
@PostMapping("/autenticacao")
public boolean autenticar(@RequestParam String login,
@RequestParam String senha,
HttpSession sessao) {
if (Objects.equals(login, USUARIO_VALIDO) && Objects.equals(senha, SENHA_VALIDA)) {
sessao.setAttribute("usuarioLogado", login);
return true;
}
return false;
}
@GetMapping("/usuario-logado")
public String obterUsuarioLogado(HttpSession sessao) {
String usuario = (String) sessao.getAttribute("usuarioLogado");
return usuario != null ? usuario : "";
}
}
Código do Frontend (login.html)
<script>
function enviarLogin() {
fetch('/api/autenticacao', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
login: document.getElementById('campoLogin').value,
senha: document.getElementById('campoSenha').value
})
})
.then(resposta => resposta.json())
.then(sucesso => {
if (sucesso) {
window.location.href = '/painel.html';
} else {
alert('Credenciais inválidas!');
}
});
}
</script>
O arquivo painel.html faria uma requisição GET para /api/usuario-logado ao carregar para exibir o nome do usuário.
3. Livro de Visitas (留言板)
Definição do Requisito
Uma aplicação onde os usuários podem postar mensagens curtas (de, para, mensagem) e visualizar todas as posatgens em ordem cronológica.
Definição das APIs
Endpoint para Postar Mensagem
- Caminho:
/api/mensagens - Método: POST
- Corpo (JSON):
{"remetente": "Ana", "destinatario": "Beto", "conteudo": "Olá!"} - Resposta: Status HTTP 201 Created.
Endpoint para Listar Mensagens
- Caminho:
/api/mensagens - Método: GET
- Resposta: Um array JSON com todas as mensagens.
Código do Backend
Modelo de Dados (DTO)
package com.exemplo.demo.modelo;
public class RegistroMensagem {
private String remetente;
private String destinatario;
private String conteudo;
// Construtores, Getters e Setters podem ser gerados pelo Lombok com @Data
}
Controlador
package com.exemplo.demo.controlador;
import com.exemplo.demo.modelo.RegistroMensagem;
import com.exemplo.demo.servico.ServicoMensagens;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/mensagens")
public class ControladorMensagens {
private final ServicoMensagens servico;
public ControladorMensagens(ServicoMensagens servico) {
this.servico = servico;
}
@GetMapping
public List<RegistroMensagem> listarTodas() {
return servico.obterTodasMensagens();
}
@PostMapping
public ResponseEntity<Void> criar(@RequestBody RegistroMensagem novaMensagem) {
servico.adicionarMensagem(novaMensagem);
return ResponseEntity.status(201).build();
}
}
Serviço (Camada de Lógica)
package com.exemplo.demo.servico;
import com.exemplo.demo.modelo.RegistroMensagem;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ServicoMensagens {
private final List<RegistroMensagem> armazenamento = new ArrayList<>();
public List<RegistroMensagem> obterTodasMensagens() {
return new ArrayList<>(armazenamento); // Retorna cópia
}
public void adicionarMensagem(RegistroMensagem mensagem) {
// Validação simples
if (mensagem.getRemetente() == null || mensagem.getConteudo() == null) {
throw new IllegalArgumentException("Campos obrigatórios não preenchidos");
}
armazenamento.add(mensagem);
}
}
Esta implementação simples utiliza uma lista em memória. Para persistência real, seria necessário integrar um repositório (como Spring Data JPA).
Simplificando com Lombok
No exemplo acima, a classe RegistroMensagem precisaria manualmente de getters, setters, construtores, etc. A biblioteca Lombok automatiza isso via anotações, reduzindo drasticamente o código boilerplate.
Principais Anotações
@Data: Gera getters, setters, toString, equals, hashCode e um construtor obrigatório para campos finais.@Getter/@Setter: Gera apenas os respectivos métodos.@AllArgsConstructor/@NoArgsConstructor: Gera construtores com todos ou nenhum argumento.
Configuração no Projeto
Adicione a dependência ao seu pom.xml (Maven):
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Para o IDE reconhecer as anotações, geralmente é necessário instalar o plugin do Lombok.
Como Fucniona
O Lombok é um processador de anotações em tempo de compilação. Ele lê as anotações no código fonte e gera automaticamente os métodos adicionais nos arquivos .class durante a compilação. O código final no bytecode é idêntico ao que você escreveria manualmente, sem nenhum impacto na performance em tempo de execução.
Exemplo com Lombok
import lombok.Data;
@Data
public class RegistroMensagem {
private String remetente;
private String destinatario;
private String conteudo;
}
// A classe agora automaticamente tem todos os getters, setters, etc.