Pular para o conteúdo

Este material complementa os slides do Tópico 08 e apresenta os conceitos essenciais para construir APIs HTTP com Spring Web no ecossistema Java.

O foco não está apenas nas anotações, mas em compreender como o Spring organiza responsabilidades, recebe requisições, converte dados, aplica validações e devolve respostas coerentes para clientes web e mobile.

O Spring é um ecossistema de frameworks para desenvolvimento em Java. Ele nasceu com a proposta de reduzir acoplamento, organizar melhor as aplicações e facilitar a manutenção de sistemas corporativos.

Hoje, o Spring é muito usado para:

  • APIs REST;
  • aplicações web;
  • sistemas corporativos;
  • integrações com banco de dados;
  • arquiteturas distribuídas e microsserviços.

Dentro desse ecossistema, o módulo Spring Web oferece suporte à construção de aplicações baseadas em HTTP.

É importante diferenciar dois conceitos relacionados, mas não idênticos.

  • Spring Framework: conjunto de módulos e abstrações que compõem a base do ecossistema.
  • Spring Boot: camada que simplifica configuração, inicialização e empacotamento de aplicações Spring.

Com Spring Boot, é comum ter:

  • configuração automática;
  • dependências agrupadas em starters;
  • servidor embutido, como Tomcat;
  • execução simplificada com poucos arquivos de configuração.

Em termos práticos: muitas APIs modernas em Java são construídas com Spring Web dentro de um projeto Spring Boot.

O Spring Web facilita tarefas frequentes do backend:

  • mapear rotas com anotações;
  • receber parâmetros da URL, query string e corpo da requisição;
  • converter JSON para objetos Java e vice-versa;
  • validar dados de entrada;
  • controlar códigos de status, cabeçalhos e corpo de resposta;
  • integrar facilmente com outras camadas da aplicação.

Isso permite que o desenvolvedor foque mais na regra de negócio e menos em detalhes repetitivos de infraestrutura HTTP.

Ao criar uma aplicação backend, algumas dependências aparecem com frequência:

  • spring-boot-starter-web: base para aplicações HTTP e APIs REST;
  • spring-boot-starter-validation: integração com Bean Validation;
  • spring-boot-starter-data-jpa: persistência com JPA/Hibernate;
  • spring-boot-devtools: melhoria da experiência de desenvolvimento.

Exemplo simplificado em pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Uma organização comum em aplicações Spring separa responsabilidades em camadas:

src/main/java/com/exemplo/app/
controller/
service/
repository/
model/
dto/
Application.java

Papéis típicos:

  • controller: recebe requisições e monta respostas;
  • service: concentra regras de negócio;
  • repository: acessa dados;
  • model ou entity: representa entidades do domínio;
  • dto: modela dados de entrada e saída.

Essa divisão melhora legibilidade, testes e evolução do projeto.

Todo projeto Spring Boot costuma ter uma classe principal anotada com @SpringBootApplication.

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Essa anotação reúne configurações importantes e inicia o contexto da aplicação, incluindo os componentes que serão gerenciados pelo Spring.

Os controllers são responsáveis por receber requisições HTTP e delegar o trabalho para outras camadas.

@RestController
@RequestMapping("/produtos")
public class ProdutoController {
}
  • @RestController indica que os métodos retornam dados diretamente no corpo da resposta;
  • @RequestMapping define um caminho base para os endpoints da classe.

O Spring fornece anotações específicas para os métodos HTTP mais comuns.

@GetMapping
public List<Produto> listar() {
return service.listar();
}
@PostMapping
public Produto criar(@RequestBody Produto produto) {
return service.criar(produto);
}

Nesse exemplo:

  • @GetMapping responde a requisições GET;
  • @PostMapping responde a requisições POST;
  • @RequestBody converte o JSON recebido em um objeto Java.

Também existem anotações como @PutMapping, @PatchMapping e @DeleteMapping.

Além do corpo da requisição, APIs frequentemente recebem informações na URL.

Usado para capturar partes dinâmicas do caminho.

@GetMapping("/{id}")
public Produto buscarPorId(@PathVariable Long id) {
return service.buscarPorId(id);
}

Se a URL for /produtos/10, o valor 10 será recebido em id.

Usado para capturar parâmetros da query string.

@GetMapping("/buscar")
public List<Produto> buscarPorNome(@RequestParam String nome) {
return service.buscarPorNome(nome);
}

Exemplo de chamada:

GET /produtos/buscar?nome=mouse HTTP/1.1

Em vários casos, devolver apenas o objeto não é suficiente. Também pode ser necessário controlar:

  • código de status;
  • cabeçalhos;
  • corpo da resposta.

Para isso, o Spring oferece ResponseEntity.

@DeleteMapping("/{id}")
public ResponseEntity<Void> remover(@PathVariable Long id) {
service.remover(id);
return ResponseEntity.noContent().build();
}

Isso é útil para retornar respostas como:

  • 200 OK;
  • 201 Created;
  • 204 No Content;
  • 400 Bad Request;
  • 404 Not Found.

Um dos pilares do Spring é o gerenciamento de objetos pelo container. Em vez de criar manualmente todas as dependências com new, o framework pode injetá-las automaticamente.

@Service
public class ProdutoService {
private final ProdutoRepository repository;
public ProdutoService(ProdutoRepository repository) {
this.repository = repository;
}
}

Vantagens:

  • menor acoplamento;
  • código mais fácil de testar;
  • centralização da criação de componentes.

A injeção por construtor é a abordagem mais recomendada em aplicações modernas com Spring.

Uma API bem organizada evita concentrar tudo no controller.

Recebe a requisição, interpreta dados de entrada e define a resposta HTTP.

Implementa a regra de negócio, valida regras do domínio e coordena operações.

Responsável por acesso a banco de dados ou mecanismos de persistência.

  • Entity ou Model: representa objetos do domínio;
  • DTO: estrutura dados enviados ou recebidos pela API.

Em APIs públicas, costuma ser melhor expor DTOs do que retornar entidades diretamente.

Validar a entrada é essencial para evitar dados inconsistentes e mensagens de erro pouco claras.

public class ProdutoDTO {
@NotBlank
private String nome;
@Positive
private BigDecimal preco;
}

No controller:

public Produto criar(@Valid @RequestBody ProdutoDTO dto) {
return service.criar(dto);
}
  • @Valid ativa a validação do objeto;
  • @NotBlank impede texto vazio;
  • @Positive exige valor numérico positivo.

Essas validações ajudam a devolver respostas 400 Bad Request de forma consistente.

Quando um método em um @RestController retorna um objeto Java, o Spring normalmente serializa esse objeto em JSON usando bibliotecas como Jackson.

@GetMapping("/{id}")
public ProdutoDTO detalhar(@PathVariable Long id) {
return service.detalhar(id);
}

Resposta possível:

{
"id": 10,
"nome": "Teclado",
"preco": 199.90
}

Da mesma forma, o Spring pode desserializar JSON recebido em objetos Java quando usamos @RequestBody.

Em vez de espalhar try-catch por todos os controllers, é comum centralizar tratamento de erros com @RestControllerAdvice.

@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(ProdutoNaoEncontradoException.class)
public ResponseEntity<String> tratarNaoEncontrado() {
return ResponseEntity.status(404).body("Produto nao encontrado");
}
}

Isso permite:

  • padronizar respostas de erro;
  • reduzir repeticao;
  • tornar a API mais previsivel para clientes.

Em projetos maiores, vale retornar um objeto estruturado de erro, com campos como timestamp, status, erro e mensagem.

Arquivos comuns de configuração:

  • src/main/resources/application.properties
  • src/main/resources/application.yml

Exemplo:

spring.application.name=loja-api
server.port=8080
spring.datasource.url=jdbc:h2:mem:teste

Esses arquivos concentram configurações como:

  • nome da aplicação;
  • porta do servidor;
  • conexão com banco de dados;
  • logs;
  • perfis de ambiente.

Uma forma simplificada de visualizar o processo:

  1. O cliente envia uma requisição HTTP.
  2. O Spring identifica o controller e o endpoint correspondente.
  3. Parâmetros e corpo são convertidos para tipos Java.
  4. A camada de service executa a regra de negócio.
  5. O repository consulta ou persiste dados, quando necessário.
  6. O resultado é transformado em resposta HTTP.
  7. O corpo normalmente é serializado em JSON.

Esse fluxo explica por que separar responsabilidades ajuda tanto na manutenção do projeto.

Requisição:

POST /produtos HTTP/1.1
Content-Type: application/json
{
"nome": "Mouse",
"preco": 89.90
}

Resposta possível:

HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 1,
"nome": "Mouse",
"preco": 89.90
}
  • Manter controllers finos e services mais expressivos.
  • Usar DTOs para entrada e saída.
  • Validar dados recebidos.
  • Retornar códigos HTTP coerentes.
  • Padronizar tratamento de exceções.
  • Evitar expor detalhes internos de banco ou implementação.

Neste tópico você viu como o Spring Web simplifica a construção de APIs HTTP em Java. Com anotações, conversão automática de JSON, validação, injeção de dependência e organização em camadas, o framework oferece uma base sólida para projetos backend.

Mais do que decorar anotações, o ponto central é entender a separação de responsabilidades: o controller conversa com o mundo HTTP, o service concentra as regras e o repository cuida da persistência.