Spring Boot Annotations:
Master essential Spring Boot annotations with practical examples. Learn @SpringBootApplication, dependency injection, REST controllers, and configuration annotations.
What You'll Learn
Build Spring Boot applications faster by mastering the annotations you'll use every day. This guide covers:
- Core application setup with
@SpringBootApplication
- Component management with stereotype annotations
- Dependency injection patterns
- REST API development annotations
- Configuration and environment management
- Data validation techniques
Prerequisites
Basic Java knowledge and familiarity with Spring Boot project structure.
Try Kodaschool for free
Click below to sign up and get access to free web, android and iOs challenges.
Key Terms We'll Use
- Annotation: Metadata providing information to the Spring framework
- Bean: An object managed by the Spring container
- Dependency Injection (DI): Spring automatically provides objects your code needs
- Component Scanning: Spring automatically discovers and registers beans
- Stereotype: Special annotations that categorize Spring components
Why Annotations Matter
Annotations are instructions that tell Spring how to manage your application.
Think of them as labels that help Spring organize and wire your application automatically.
Spring annotations eliminate XML configuration and reduce boilerplate code. They tell Spring how to manage your objects, wire dependencies, and configure your application.
Understanding annotations is essential for building modern Spring applications efficiently.
Application Bootstrap
@SpringBootApplication
This single annotation starts your entire application. It combines three annotations:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
What it does:
- Marks the class as a configuration source
- Enables Spring Boot's auto-configuration
- Scans for components( component scanning) in the current package and subpackages
When to use it: Every Spring Boot application needs exactly one class with this annotation.
Component Management
Spring uses stereotype annotations to identify and manage different types of components.
@Component
Marks any class as a Spring-managed component.
@Component
public class EmailValidator {
public boolean isValid(String email) {
return email.contains("@");
}
}
When to use it: For utility classes that don't fit other categories.
@Service
Marks business logic classes.
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, String email) {
User user = new User(name, email);
return userRepository.save(user);
}
}
When to use it: For classes containing business logic and operations.
@Repository
Marks data access classes. Automatically translates database exceptions into Spring's exception hierarchy.
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
}
When to use it: For classes that interact with databases.
@Controller vs @RestController
@Controller returns view names for rendering HTML pages:
@Controller
public class WebController {
@GetMapping("/home")
public String homePage(Model model) {
model.addAttribute("message", "Welcome!");
return "home"; // Returns view name
}
}
@RestController returns data directly as JSON or XML:
@RestController
public class UserApiController {
@GetMapping("/api/users")
public List<User> getUsers() {
return userService.getAllUsers(); // Returns JSON
}
}
When to use them:
- Use
@Controller
for server-side rendered pages - Use
@RestController
for REST APIs
Dependency Injection
@Autowired
Tells Spring to inject dependencies automatically. Constructor injection is recommended over field injection.
Constructor injection (recommended):
@Service
public class OrderService {
private final UserService userService;
private final EmailService emailService;
public OrderService(UserService userService, EmailService emailService) {
this.userService = userService;
this.emailService = emailService;
}
}
Field injection (avoid when possible):
@Service
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private EmailService emailService;
}
Why constructor injection? It makes dependencies explicit, enables immutability with final
, and makes testing easier.
@Qualifier
Specifies which bean to inject when multiple beans of the same type exist.
@Configuration
public class NotificationConfig {
@Bean
@Qualifier("email")
public NotificationService emailService() {
return new EmailNotificationService();
}
@Bean
@Qualifier("sms")
public NotificationService smsService() {
return new SmsNotificationService();
}
}
@Service
public class AlertService {
private final NotificationService notificationService;
public AlertService(@Qualifier("email") NotificationService notificationService) {
this.notificationService = notificationService;
}
}
REST API Development
HTTP Method Annotations
Map HTTP requests to handler methods.
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping
public User createUser(@RequestBody CreateUserRequest request) {
return userService.create(request);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody UpdateUserRequest request) {
return userService.update(id, request);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
}
Available annotations:
@GetMapping
- Retrieve data@PostMapping
- Create new resources@PutMapping
- Update entire resources@PatchMapping
- Update partial resources@DeleteMapping
- Remove resources
@PathVariable
Extracts values from the URL path.
@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(
@PathVariable Long userId,
@PathVariable Long orderId
) {
return orderService.findByUserAndOrder(userId, orderId);
}
@RequestParam
Extracts query parameters from the URL.
@GetMapping("/search")
public List<User> searchUsers(
@RequestParam String name,
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "10") int size
) {
return userService.search(name, page, size);
}
Example request: /search?name=John&page=2&size=20
@RequestBody
Binds the HTTP request body to a method parameter.
@PostMapping("/users")
public User createUser(@RequestBody CreateUserRequest request) {
return userService.create(request);
}
public class CreateUserRequest {
private String name;
private String email;
private int age;
// Getters and setters
}
Configuration Management
@Configuration and @Bean
Create and configure beans manually when you need fine-grained control.
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(
@Value("${db.url}") String url,
@Value("${db.username}") String username,
@Value("${db.password}") String password
) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(10);
return new HikariDataSource(config);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
When to use it: For third-party libraries, complex initialization logic, or when you need multiple beans of the same type.
@Value
Injects configuration values from application.properties
or environment variables.
@Service
public class ApiService {
@Value("${api.url}")
private String apiUrl;
@Value("${api.timeout:30}")
private int timeoutSeconds;
@Value("${api.key}")
private String apiKey;
}
In application.properties:
api.url=https://api.example.com
api.timeout=60
api.key=${API_KEY}
Default values: Use the colon syntax ${property:defaultValue}
to provide defaults.
Environment-Specific Configuration
@Profile
Create beans only when specific profiles are active.
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(System.getenv("DATABASE_URL"));
return new HikariDataSource(config);
}
}
Activate profiles:
# In application.properties
spring.profiles.active=dev
# Or via command line
java -jar app.jar --spring.profiles.active=prod
# Or via environment variable
SPRING_PROFILES_ACTIVE=prod
@ConditionalOnProperty
Create beans based on configuration properties.
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnProperty(
name = "cache.enabled",
havingValue = "true",
matchIfMissing = false
)
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users", "products");
}
}
Data Validation
@Valid and Validation Annotations
Validate request data automatically.
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public User createUser(@Valid @RequestBody CreateUserRequest request) {
return userService.create(request);
}
}
public class CreateUserRequest {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
@Min(value = 18, message = "Must be at least 18 years old")
@Max(value = 120, message = "Age must be realistic")
private int age;
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$", message = "Invalid phone number")
private String phone;
}
Common validation annotations:
@NotNull
- Value cannot be null@NotBlank
- String cannot be null or empty@NotEmpty
- Collection cannot be null or empty@Size
- String or collection size constraints@Min
/@Max
- Numeric range validation@Email
- Valid email format@Pattern
- Regex pattern matching@Past
/@Future
- Date validation
Best Practices
Use Constructor Injection
Constructor injection makes dependencies explicit and enables immutability.
Good:
@Service
public class OrderService {
private final UserService userService;
private final ProductService productService;
public OrderService(UserService userService, ProductService productService) {
this.userService = userService;
this.productService = productService;
}
}
Avoid:
@Service
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
}
Choose Specific Stereotype Annotations
Use the most specific annotation for clarity.
- Business logic →
@Service
- Data access →
@Repository
- Web layer →
@Controller
or@RestController
- Configuration →
@Configuration
- Generic utilities →
@Component
Organize REST Controllers
Use @RequestMapping
at the class level for base paths.
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
@GetMapping // Maps to /api/v1/orders
public List<Order> getOrders() {
return orderService.findAll();
}
@GetMapping("/{id}") // Maps to /api/v1/orders/{id}
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
@PostMapping // Maps to /api/v1/orders
public Order createOrder(@Valid @RequestBody CreateOrderRequest request) {
return orderService.create(request);
}
}
Quick Reference

Key Takeaways
@SpringBootApplication launches your application with minimal configuration.
Stereotype annotations help Spring organize your code into manageable components.
Constructor injection makes dependencies explicit and code easier to test.
REST annotations map HTTP requests to your business logic cleanly.
@Configuration and @Bean give you manual control when auto-configuration isn't enough.
Validation annotations ensure data integrity without writing boilerplate code.
Next Steps
Start building by:
- Creating a REST controller with
@RestController
- Implementing a service layer with
@Service
- Adding data validation with
@Valid
and validation annotations - Configuring environment-specific beans with
@Profile
Additional Resources:
Happy Coding