Pular para o conteúdo principal

Capítulo 4: Anotações Básicas do Spring Boot

Introdução​

As anotações estão no coração do Spring Boot. Elas permitem configurar a aplicação de forma declarativa, sem XML, tornando o código mais legível e fácil de manter.

Objetivos do Capítulo​

Ao final deste capítulo, você será capaz de:

  • Compreender o papel das principais anotações do Spring Boot
  • Utilizar @RestController, @Service, @Repository
  • Dominar as anotações de mapeamento HTTP
  • Injetar dependências com @Autowired
  • Criar beans Spring

O que é uma Anotação?​

Definição​

Uma anotação é uma forma de metadado que fornece informações sobre o código sem fazer parte dele diretamente.

Formato:

@NomeAnotacao
@NomeAnotacao(parametro=valor)

Exemplo:

@RestController
@RequestMapping("/api")
public class UserController {
// ...
}

Por que usar Anotações?​

Vantagens:

  • Código mais conciso
  • Configuração próxima ao código
  • Type-safe (verificação em tempo de compilação)
  • Facilita o refatoramento

@SpringBootApplication​

A Anotação Mestra​

package com.example.greeting;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GreetingApplication {

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

Composição do @SpringBootApplication​

@SpringBootApplication é uma meta-anotação que combina três anotações:

@SpringBootApplication
=
@SpringBootConfiguration
+ @EnableAutoConfiguration
+ @ComponentScan

Detalhe de Cada Anotação​

1. @SpringBootConfiguration​

Papel: Marca a classe como fonte de definições de beans

Equivalente a:

@Configuration

Permite definir beans:

@SpringBootApplication
public class GreetingApplication {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

2. @EnableAutoConfiguration​

Papel: Ativa a configuração automática do Spring Boot

Exemplo: Se o Spring Boot detectar:

  • spring-boot-starter-web → Configura Tomcat, Spring MVC
  • spring-boot-starter-data-jpa → Configura Hibernate, DataSource
  • PostgreSQL Driver → Configura PostgreSQL

Configuração automática:

3. @ComponentScan​

Papel: Escaneia o pacote e subpacotes em busca de componentes Spring

Pacote escaneado:

package com.example.greeting;  // Pacote base

// O Spring irá escanear:
// - com.example.greeting
// - com.example.greeting.controller
// - com.example.greeting.service
// - etc.

Componentes detectados:

  • @Component
  • @Controller / @RestController
  • @Service
  • @Repository
  • @Configuration

Anotações de Estereótipo​

Hierarquia de Componentes​

@Component​

Papel: Anotação genérica para qualquer componente Spring

Uso:

import org.springframework.stereotype.Component;

@Component
public class EmailValidator {

public boolean isValid(String email) {
return email.contains("@");
}
}

Quando usar: Para classes utilitárias que não se encaixam em outras categorias

@Controller​

Papel: Marca uma classe como controlador MVC (retorna views)

Uso:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

@GetMapping("/")
public String home(Model model) {
model.addAttribute("message", "Bem-vindo!");
return "home"; // Retorna a view "home.html"
}
}

Arquitetura:

@RestController​

Papel: Controlador REST (retorna dados JSON/XML)

Equivalente a:

@RestController
=
@Controller + @ResponseBody

Uso:

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
public class GreetingController {

@GetMapping("/greeting")
public Greeting greeting() {
return new Greeting(1L, "Hello, World!");
}
}

Arquitetura:

Diferença @Controller vs @RestController:

Aspecto@Controller@RestController
RetornoNome da view (String)Dados (JSON/XML)
UsoAplicações web tradicionaisREST APIs
SerializaçãoNãoAutomática (Jackson)
@ResponseBodyNecessário em cada métodoImplícito

@Service​

Papel: Marca uma classe que contém a lógica de negócio

Uso:

import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class GreetingService {

private final AtomicLong counter = new AtomicLong();
private static final String template = "Hello, %s!";

public Greeting createGreeting(String name) {
return new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);
}
}

Responsabilidades:

  • Lógica de negócio
  • Cálculos complexos
  • Orquestração de repositórios
  • Transações

@Repository​

Papel: Marca uma classe de acesso a dados

Uso com JPA:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// O Spring Data JPA gera automaticamente a implementação
}

Responsabilidades:

  • Consultas ao banco de dados
  • Operações CRUD
  • Queries customizadas

Vantagem: Tradução automática de exceções SQL em exceções Spring


Anotações de Mapeamento HTTP​

Visão Geral​

@RequestMapping​

Papel: Anotação genérica para mapear requisições HTTP

Sintaxe:

@RequestMapping(value = "/path", method = RequestMethod.GET)

Uso no nível da classe:

@RestController
@RequestMapping("/api/users")
public class UserController {

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable Long id) {
// GET /api/users/123
return userService.findById(id);
}
}

@GetMapping​

Papel: Atalho para requisições GET

Equivalente a:

@GetMapping("/path")
=
@RequestMapping(value = "/path", method = RequestMethod.GET)

Exemplos:

@RestController
@RequestMapping("/api")
public class ProductController {

// GET /api/products
@GetMapping("/products")
public List<Product> getAllProducts() {
return productService.findAll();
}

// GET /api/products/123
@GetMapping("/products/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.findById(id);
}

// GET /api/products?category=books
@GetMapping("/products")
public List<Product> getProductsByCategory(@RequestParam String category) {
return productService.findByCategory(category);
}
}

@PostMapping​

Papel: Atalho para requisições POST (criação)

Uso:

@RestController
@RequestMapping("/api/users")
public class UserController {

@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

Requisição:

POST /api/users
Content-Type: application/json

{
"name": "John Doe",
"email": "john@example.com"
}

@PutMapping​

Papel: Atalho para requisições PUT (atualização completa)

Uso:

@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}

Requisição:

PUT /api/users/123
Content-Type: application/json

{
"name": "John Doe Updated",
"email": "john.updated@example.com"
}

@DeleteMapping​

Papel: Atalho para requisições DELETE (remoção)

Uso:

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}

Requisição:

DELETE /api/users/123

@PatchMapping​

Papel: Atalho para requisições PATCH (atualização parcial)

Uso:

@PatchMapping("/{id}")
public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
return userService.partialUpdate(id, updates);
}

Requisição:

PATCH /api/users/123
Content-Type: application/json

{
"email": "newemail@example.com"
}

Tabela Resumo​

Método HTTPAnotaçãoAçãoIdempotente
GET@GetMappingLer✅
POST@PostMappingCriar❌
PUT@PutMappingAtualizar (completo)✅
PATCH@PatchMappingAtualizar (parcial)❌
DELETE@DeleteMappingRemover✅

Anotações de Parâmetros​

@PathVariable​

Papel: Extrair um valor da URL

Sintaxe:

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// URL: /users/123
// id = 123
return userService.findById(id);
}

Várias variáveis:

@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(
@PathVariable Long userId,
@PathVariable Long orderId
) {
return orderService.findByUserAndId(userId, orderId);
}

Nome diferente:

@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long userId) {
return userService.findById(userId);
}

@RequestParam​

Papel: Extrair parâmetros da query string

Sintaxe:

@GetMapping("/users")
public List<User> getUsers(@RequestParam String name) {
// URL: /users?name=John
// name = "John"
return userService.findByName(name);
}

Parâmetro opcional:

@GetMapping("/users")
public List<User> getUsers(
@RequestParam(required = false) String name
) {
if (name != null) {
return userService.findByName(name);
}
return userService.findAll();
}

Valor padrão:

@GetMapping("/users")
public List<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
return userService.findAll(page, size);
}

Vários parâmetros:

@GetMapping("/users")
public List<User> searchUsers(
@RequestParam String name,
@RequestParam int age,
@RequestParam String city
) {
// URL: /users?name=John&age=30&city=Montreal
return userService.search(name, age, city);
}

@RequestBody​

Papel: Mapear o corpo da requisição HTTP para um objeto Java

Uso:

@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.save(user);
}

Requisição HTTP:

POST /users
Content-Type: application/json

{
"name": "John Doe",
"email": "john@example.com",
"age": 30
}

Objeto Java recebido:

User user = new User();
user.setName("John Doe");
user.setEmail("john@example.com");
user.setAge(30);

@RequestHeader​

Papel: Extrair um header HTTP

Uso:

@GetMapping("/secure")
public String secureEndpoint(
@RequestHeader("Authorization") String token
) {
// token = "Bearer eyJhbGciOiJIUzI1NiIs..."
return "Autenticado!";
}

Tabela Comparativa​

AnotaçãoFonteExemplo URLUso
@PathVariableCaminho da URL/users/123Identificadores de recursos
@RequestParamQuery string/users?name=JohnFiltros, paginação
@RequestBodyCorpo HTTPPOST/PUT bodyEnvio de dados
@RequestHeaderHeaders HTTPAuthorization: Bearer tokenAutenticação

Injeção de Dependências​

@Autowired​

Papel: Injeta automaticamente uma dependência

Tipos de Injeção​

1. Injeção por Construtor (Recomendada)​

@RestController
public class UserController {

private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}
}

Desde o Spring 4.3: @Autowired é opcional se houver apenas um construtor

@RestController
public class UserController {

private final UserService userService;

// @Autowired não é necessário
public UserController(UserService userService) {
this.userService = userService;
}
}

Vantagens:

  • Imutabilidade (final)
  • Facilita os testes
  • Obrigatório (sem NullPointerException)

2. Injeção por Setter​

@RestController
public class UserController {

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}

Vantagens:

  • Dependências opcionais
  • Possível reconfiguração

Desvantagens:

  • Mutável
  • Risco de NullPointerException

3. Injeção por Campo (Não Recomendada)​

@RestController
public class UserController {

@Autowired
private UserService userService;
}

Desvantagens:

  • Difícil de testar
  • Acoplamento forte
  • Impossível criar objeto imutável

Recomendação: Sempre utilize injeção por construtor

Exemplo Completo de Arquitetura​

// Entidade
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters/Setters
}

// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

// Service
@Service
public class UserService {

private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public List<User> findAll() {
return userRepository.findAll();
}

public User save(User user) {
return userRepository.save(user);
}
}

// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}

@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

Fluxo das dependências:


Exercícios Práticos​

Exercício 1: Criar um Controlador REST​

Objetivo: Utilizar @RestController e @GetMapping

Instruções:

  1. Crie uma classe HelloController
  2. Anote com @RestController
  3. Crie um método sayHello() que retorna "Hello, Spring Boot!"
  4. Anote com @GetMapping("/hello")
  5. Teste no navegador

Exercício 2: Usar @PathVariable​

Objetivo: Extrair parâmetros da URL

Instruções:

  1. Crie um endpoint /greet/\{name\}
  2. Retorne "Hello, {name}!"
  3. Teste com /greet/John

Exercício 3: Criar um Service​

Objetivo: Separar a lógica de negócio

Instruções:

  1. Crie uma classe CalculatorService com @Service
  2. Adicione um método add(int a, int b)
  3. Crie um CalculatorController
  4. Injete o service via construtor
  5. Crie um endpoint /add?a=5&b=3

Exercício 4: Arquitetura Completa​

Objetivo: Criar uma arquitetura em 3 camadas

Instruções:

  1. Crie uma classe Product (model)
  2. Crie ProductService com uma lista em memória
  3. Crie ProductController
  4. Implemente:
    • GET /products (todos)
    • GET /products/{id}
    • POST /products

Perguntas de Revisão​

  1. Quais anotações compõem o @SpringBootApplication?

  2. Qual a diferença entre @Controller e @RestController?

  3. Quando usar @Service vs @Component?

  4. Qual a diferença entre @PathVariable e @RequestParam?

  5. Por que a injeção por construtor é recomendada?

  6. O que faz a anotação @RequestBody?

  7. Qual método HTTP corresponde ao @PostMapping?

  8. Para que serve @Repository?


Resumo do Capítulo​

Pontos-Chave​

  • @SpringBootApplication combina configuração, auto-configuração e scan
  • @RestController = @Controller + @ResponseBody
  • @Service para lógica de negócio, @Repository para acesso a dados
  • Anotações de mapeamento: @GetMapping, @PostMapping, etc.
  • @PathVariable para a URL, @RequestParam para query strings
  • Injeção por construtor recomendada

Boas Práticas​

  • Usar anotações especializadas (@Service, @Repository) ao invés de @Component
  • Injeção por construtor com final
  • Um controlador por recurso REST
  • Separar claramente as responsabilidades (Controller → Service → Repository)
  • Nomear os endpoints de forma consistente (convenções REST)

Próximos Passos​

Parabéns! Você concluiu o Módulo 2.

No Módulo 3 (em breve), você irá aprofundar em:

  • Inversão de Controle em detalhes
  • O ciclo de vida dos beans
  • Os escopos
  • Configuração avançada

Colocando em Prática​

Antes de passar para o próximo módulo, certifique-se de:

  • Completar o TP2.1: Aplicação Greeting
  • Refazer os exercícios de cada capítulo
  • Criar seu próprio mini-projeto para consolidar o aprendizado

Voltar ao Módulo 2