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 MVCspring-boot-starter-data-jpa→ Configura Hibernate, DataSourcePostgreSQL 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 |
|---|---|---|
| Retorno | Nome da view (String) | Dados (JSON/XML) |
| Uso | Aplicações web tradicionais | REST APIs |
| Serialização | Não | Automática (Jackson) |
| @ResponseBody | Necessário em cada método | Implí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 HTTP | Anotação | Ação | Idempotente |
|---|---|---|---|
| GET | @GetMapping | Ler | ✅ |
| POST | @PostMapping | Criar | ❌ |
| PUT | @PutMapping | Atualizar (completo) | ✅ |
| PATCH | @PatchMapping | Atualizar (parcial) | ❌ |
| DELETE | @DeleteMapping | Remover | ✅ |
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ção | Fonte | Exemplo URL | Uso |
|---|---|---|---|
| @PathVariable | Caminho da URL | /users/123 | Identificadores de recursos |
| @RequestParam | Query string | /users?name=John | Filtros, paginação |
| @RequestBody | Corpo HTTP | POST/PUT body | Envio de dados |
| @RequestHeader | Headers HTTP | Authorization: Bearer token | Autenticaçã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:
- Crie uma classe
HelloController - Anote com
@RestController - Crie um método
sayHello()que retorna "Hello, Spring Boot!" - Anote com
@GetMapping("/hello") - Teste no navegador
Exercício 2: Usar @PathVariable
Objetivo: Extrair parâmetros da URL
Instruções:
- Crie um endpoint
/greet/\{name\} - Retorne "Hello, {name}!"
- Teste com
/greet/John
Exercício 3: Criar um Service
Objetivo: Separar a lógica de negócio
Instruções:
- Crie uma classe
CalculatorServicecom@Service - Adicione um método
add(int a, int b) - Crie um
CalculatorController - Injete o service via construtor
- Crie um endpoint
/add?a=5&b=3
Exercício 4: Arquitetura Completa
Objetivo: Criar uma arquitetura em 3 camadas
Instruções:
- Crie uma classe
Product(model) - Crie
ProductServicecom uma lista em memória - Crie
ProductController - Implemente:
- GET /products (todos)
- GET /products/{id}
- POST /products
Perguntas de Revisão
-
Quais anotações compõem o @SpringBootApplication?
-
Qual a diferença entre @Controller e @RestController?
-
Quando usar @Service vs @Component?
-
Qual a diferença entre @PathVariable e @RequestParam?
-
Por que a injeção por construtor é recomendada?
-
O que faz a anotação @RequestBody?
-
Qual método HTTP corresponde ao @PostMapping?
-
Para que serve @Repository?
Resumo do Capítulo
Pontos-Chave
@SpringBootApplicationcombina configuração, auto-configuração e scan@RestController=@Controller+@ResponseBody@Servicepara lógica de negócio,@Repositorypara acesso a dados- Anotações de mapeamento:
@GetMapping,@PostMapping, etc. @PathVariablepara a URL,@RequestParampara 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