Spring Annotations Cheat Sheet: Every Annotation You Need to Know (With Real Code Examples)
Spring Framework is an annotation-driven framework. @Service, @Autowired, @RestController, @Value, @Profile - these annotations are not decorative labels. They are instructions to the Spring IoC container, the web layer, the configuration system, and the validation engine. Understanding what each annotation does - and more importantly, when to use it - is what separates developers who use Spring from developers who understand it.
Most developers learn Spring annotations by copying patterns from tutorials without understanding the purpose behind them. They know @Service goes on service classes, but they don't know why @Service exists when @Component does the same job. They use @Autowired everywhere but can't explain when @Qualifier is needed. They've seen @Value but have never used @ConfigurationProperties. This guide closes all of those gaps.
This cheat sheet covers every commonly used Spring annotation, grouped by purpose, with a real code example for each group and a full quick-reference table at the end. It's designed to be bookmarked - come back to it every time an annotation confuses you. For a broader Spring foundation, Board Infinity's guide on core Java concepts and syntax and their overview of OOP concepts in Java will give you the Java knowledge that makes annotations genuinely click - Spring's annotation processing is built entirely on Java's class, interface, and reflection model.
Who This Guide Is For
This guide is for you if you:
- Are learning Spring Boot and keep encountering annotations you don't fully understand
- Can use Spring annotations by following patterns but want to understand what each one does
- Want a single reference to come back to when working on Spring projects
- Are preparing for Spring or Spring Boot technical interviews
- Want to understand the Java foundations that make annotation processing work - Board Infinity's classes and objects in Java guide explains the Java class model that Spring's component scanner reads when processing every
@Service,@Repository, and@Componentannotation
1. Stereotype Annotations: @Component, @Service, @Repository, @Controller
Stereotype annotations tell Spring's component scanner to register a class as a bean in the IoC container. All four are functionally similar - they all result in a managed Spring bean - but each carries semantic meaning and some provide additional behaviour.
@Component is the generic stereotype - use it for any Spring-managed class that doesn't fit the other three. @Service marks business logic classes. @Repository marks data access classes and adds automatic exception translation (converts JPA/JDBC exceptions into Spring's DataAccessException hierarchy). @Controller marks MVC controllers that return view names. @RestController is @Controller + @ResponseBody and returns JSON directly.
The distinction matters for readability, for future framework features, and for Spring's exception translation mechanism - which only activates on classes marked @Repository. The List<Product> return type on getAll() in ProductApiController uses Java generics directly. Board Infinity's generics in Java guide explains the <Product> type parameter: it tells Spring's Jackson serialiser exactly what type to include in the JSON array, giving you compile-time type safety across the full stack from @Repository to @RestController.
// @Component - generic bean, doesn't fit other stereotypes @Component public class SlugGenerator { public String generate(String title) { return title.toLowerCase().replace(" ", "-"); } } // @Service - business logic layer @Service public class ProductService { // business rules, orchestration, transaction logic } // @Repository - data access layer // Spring adds automatic JPA exception translation to this layer @Repository public class ProductRepositoryImpl implements ProductRepository { // direct JPA/JDBC access - exceptions translated automatically } // @Controller - MVC controller, returns view names @Controller public class DashboardController { @GetMapping("/dashboard") public String show(Model m) { return "dashboard"; } } // @RestController - REST API, returns JSON directly @RestController // = @Controller + @ResponseBody @RequestMapping("/api/products") public class ProductApiController { @GetMapping public List<Product> getAll() { return productService.findAll(); } }
Technically, @Service, @Repository, and @Controller are all specialisations of @Component - they all register a bean. But they communicate intent to other developers, enable IDE tooling to flag misplaced classes, and allow Spring and third-party tools to apply layer-specific behaviour. @Repository specifically activates exception translation. Use the most specific annotation that fits - never just @Component when a more specific one applies.
2. Dependency Injection: @Autowired, @Qualifier, @Primary
These three annotations control how Spring resolves and injects dependencies. @Autowired tells Spring to inject a bean. @Qualifier tells Spring which bean to inject when multiple candidates exist. @Primary marks a bean as the default choice when multiple candidates exist and no @Qualifier is specified.
Understanding when each is needed requires understanding how Spring resolves injection: first by type, then by name. When exactly one bean of the required type exists, @Autowired alone works. When multiple beans of the same type exist, Spring needs a hint - either @Primary on the preferred bean or @Qualifier at the injection point.
For a deeper understanding of why this matters, Board Infinity's post on understanding polymorphism in Java explains the interface-based injection model that makes @Qualifier necessary - both EmailNotificationService and SmsNotificationService implement NotificationService, which is polymorphism in action. Spring injects through the interface type, and @Qualifier is the disambiguation mechanism when multiple implementations exist. The multiple inheritance in Java guide covers the interface inheritance model directly: NotificationService defines the contract that both implementations fulfill, and Spring's DI system is designed to inject by interface type for exactly this reason.
public interface NotificationService { void send(String recipient, String message); } // @Primary - default when no @Qualifier specified @Service @Primary public class EmailNotificationService implements NotificationService { @Override public void send(String r, String m) { /* email logic */ } } @Service @Qualifier("sms") public class SmsNotificationService implements NotificationService { @Override public void send(String r, String m) { /* SMS logic */ } } @Service public class OrderService { private final NotificationService defaultNotifier; // gets EmailNotificationService (@Primary) private final NotificationService smsNotifier; // gets SmsNotificationService (@Qualifier) public OrderService( NotificationService defaultNotifier, @Qualifier("sms") NotificationService smsNotifier) { this.defaultNotifier = defaultNotifier; this.smsNotifier = smsNotifier; } public void confirmOrder(Order order) { defaultNotifier.send(order.getEmail(), "Order confirmed"); // email smsNotifier.send(order.getPhone(), "Order confirmed"); // SMS } }
Since Spring 4.3, if a class has exactly one constructor, Spring injects its parameters automatically without needing @Autowired on the constructor. This means you can write clean constructor injection with no annotation at all. @Autowired is still required for setter injection and field injection - but for the recommended constructor injection style, it's optional and most modern Spring code omits it.
3. Configuration Annotations: @Configuration, @Bean, @ComponentScan
These annotations control how Spring assembles your application context. @Configuration marks a class as a source of bean definitions. @Bean on a method tells Spring to register that method's return value as a bean. @ComponentScan tells Spring where to look for @Component-annotated classes.
The key distinction: @Component-based beans are auto-detected by class scanning. @Bean-based beans are explicitly declared in code. Use @Bean when you need to configure a third-party class (like a library object you don't own) or when you need precise control over how a bean is constructed.
The @Bean methods in AppConfig - objectMapper(), modelMapper(), restTemplate() - return configured instances of third-party classes. The method chaining used to configure ObjectMapper - .configure(...).setSerializationInclusion(...) - is the builder pattern applied to third-party library configuration. Board Infinity's what is composition in Java guide covers how Java objects are assembled from parts - the @Configuration class uses composition to assemble the full application context from its component beans, and each @Bean method constructs one component of that composed context.
// @ComponentScan - tells Spring where to look for @Component classes // Spring Boot auto-configures this for the main package - rarely needed explicitly @ComponentScan("com.example.myapp") @Configuration public class AppConfig { // @Bean - register a third-party object as a Spring bean // Use when you can't add @Component to the class (e.g. library classes) @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); return mapper; // Spring registers this as a bean named "objectMapper" } // @Bean with dependencies - Spring injects other beans as parameters @Bean public ModelMapper modelMapper() { return new ModelMapper(); } // @Bean name - defaults to method name, can be overridden @Bean(name = "customRestTemplate") public RestTemplate restTemplate() { return new RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(5)) .build(); } }
Use @Component (and its stereotypes) on classes you own and can annotate directly. Use @Bean inside a @Configuration class when you need to register a class you don't own (third-party library), when the bean needs complex construction logic, or when you need multiple beans of the same type with different configurations. In Spring Boot, @ComponentScan is auto-configured for your main package - you only need it if you have beans outside that package.
4. Property Binding: @Value and @ConfigurationProperties
These annotations read values from application.properties or application.yml into your Spring beans. @Value injects a single property into a field or constructor parameter. @ConfigurationProperties binds an entire prefix of properties to a typed Java class - the cleaner and more maintainable approach for groups of related properties.
For anything beyond a single config value, @ConfigurationProperties is strongly preferred. It provides type safety, IDE auto-completion, validation support, and keeps related properties organised in one class rather than scattered across multiple @Value annotations.
The ApiProperties class - with private fields, getters, and setters for key, timeout, and retries - is the encapsulation principle applied to configuration management. Board Infinity's abstraction vs encapsulation in Java guide explains precisely why @ConfigurationProperties classes use private fields with getters and setters: the configuration values are encapsulated inside the class, exposed only through controlled accessors. Any service that injects ApiProperties gets the values through the getter interface - it never directly accesses the raw YAML properties. The access modifiers in Java guide covers why private fields matter here: jwtSecret and apiKey fields must be private to prevent external components from reading sensitive configuration values directly.
// application.yml properties used below: // app.name: "My Store" // app.api.key: "abc-123" // app.api.timeout: 5000 // app.api.retries: 3 // app.feature.analytics: true // @Value - injects a single property value @Service public class AppInfoService { @Value("${app.name}") private String appName; @Value("${app.feature.analytics:false}") // :false = default if property missing private boolean analyticsEnabled; } // @ConfigurationProperties - binds a group of properties to a class // Much better for related properties - type-safe, IDE-supported, validatable @ConfigurationProperties(prefix = "app.api") public class ApiProperties { private String key; private int timeout; // binds app.api.timeout private int retries; // binds app.api.retries // Spring binds app.api.key, app.api.timeout, app.api.retries automatically // getters and setters required public String getKey() { return key; } public int getTimeout() { return timeout; } public int getRetries() { return retries; } public void setKey(String k) { this.key = k; } public void setTimeout(int t){ this.timeout = t; } public void setRetries(int r){ this.retries = r; } } // Enable scanning of @ConfigurationProperties classes @SpringBootApplication @EnableConfigurationProperties(ApiProperties.class) public class Application { /* ... */ } // Inject the whole config object - clean, typed, auto-completed @Service public class ExternalApiService { private final ApiProperties apiProps; public ExternalApiService(ApiProperties apiProps) { this.apiProps = apiProps; } }
5. Web Layer Annotations: @RequestMapping, @ResponseBody, @PathVariable
These annotations handle the HTTP layer - mapping URLs, reading request data, and writing response data. @RequestMapping is the base URL mapping annotation. The shorthand versions - @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping - are more readable and preferred. @ResponseBody tells Spring to write the return value to the HTTP response body as JSON. @RestController applies @ResponseBody to every method in the class automatically.
The @PathVariable Long id in getById() and the @RequestParam String status in filter() both extract typed values from the HTTP request into Java types. Board Infinity's understanding wrapper class in Java guide explains the wrapper type Long used in @PathVariable Long id - Spring's web layer converts the String URL segment "42" to Long using Java's autoboxing, and the wrapper type rather than primitive long allows Spring to handle missing path variables without a NullPointerException. The learn about Java list guide is relevant to the List<Order> return types - Spring's Jackson serialiser converts the List<Order> directly to a JSON array, and understanding Java's List generic type explains why the serialiser can correctly introspect the Order type for each element.
@RestController // @Controller + @ResponseBody on all methods @RequestMapping("/api/orders") // base URL for all methods in this class public class OrderController { // @GetMapping - shorthand for @RequestMapping(method = GET) @GetMapping public List<Order> getAll() { return orderService.findAll(); } // @PathVariable - extracts {id} from the URL path @GetMapping("/{id}") public ResponseEntity<Order> getById(@PathVariable Long id) { return orderService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } // @RequestParam - reads query string: /api/orders?status=PENDING&page=0 @GetMapping("/filter") public List<Order> filter( @RequestParam String status, @RequestParam(defaultValue = "0") int page) { return orderService.findByStatus(status, page); } // @RequestBody - reads JSON request body into Java object @PostMapping public ResponseEntity<Order> create(@RequestBody CreateOrderRequest req) { return ResponseEntity.status(201).body(orderService.create(req)); } // @ResponseStatus - sets HTTP status without ResponseEntity @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) // returns 204 automatically public void delete(@PathVariable Long id) { orderService.delete(id); } }
For methods that always return the same HTTP status, @ResponseStatus is cleaner - no need to wrap the return type in ResponseEntity. For methods where the status depends on the result (200 if found, 404 if not), use ResponseEntity<T> which lets you set status, headers, and body dynamically. A good rule: @ResponseStatus for delete endpoints (always 204), ResponseEntity for get-by-id endpoints (200 or 404).
6. Profile and Environment: @Profile and @Conditional
These annotations control which beans are active depending on the runtime environment. @Profile activates a bean only when a specific Spring profile is active. @Conditional is the more powerful version - it activates a bean based on any custom condition you define, including the presence of a class, a property value, or an environment variable.
@Profile is used constantly in real Spring applications to switch between development (mock services, in-memory databases) and production (real services, live databases) without changing any code - just the active profile.
The @Profile example - where StripePaymentGateway is active in production and MockPaymentGateway is active in development, both implementing PaymentGateway - is the interface-based abstraction and polymorphism model applied to environment switching. Board Infinity's abstraction in Java guide covers this pattern directly: PaymentGateway defines the abstract contract, and @Profile selects the concrete implementation at runtime without the calling service knowing which one it has. The overloading vs overriding guide is also relevant - both StripePaymentGateway and MockPaymentGateway use @Override on process(), and understanding the method override contract explains why both implementations are interchangeable through the PaymentGateway interface.
public interface PaymentGateway { PaymentResult process(PaymentRequest request); } // Active in production - real Stripe integration @Service @Profile("production") public class StripePaymentGateway implements PaymentGateway { @Override public PaymentResult process(PaymentRequest req) { return stripeClient.charge(req); // real charge } } // Active in development - no real charges, safe for testing @Service @Profile("development") public class MockPaymentGateway implements PaymentGateway { @Override public PaymentResult process(PaymentRequest req) { return PaymentResult.success("mock-tx-" + req.getAmount()); } } // Set active profile in application.yml: // spring.profiles.active: development // Or via environment variable: SPRING_PROFILES_ACTIVE=production // @ConditionalOnProperty - activate bean based on property value @Service @ConditionalOnProperty(name = "app.feature.analytics", havingValue = "true") public class AnalyticsService { // Only created if app.feature.analytics=true in application.yml }
@Profile("!production") activates a bean in every profile except production. This is useful for debug tools, mock services, and test data seeders that should never run in production. Combine with @Profile({"development", "staging"}) to activate a bean in multiple specific profiles simultaneously.
7. Quick Reference Table - All Key Spring Annotations
| Annotation | Category | What It Does | When to Use |
|---|---|---|---|
@Component |
Stereotype | Registers a generic Spring bean | Any Spring-managed class that isn't a service, repo, or controller |
@Service |
Stereotype | Marks a business logic bean | Service layer classes containing business rules |
@Repository |
Stereotype | Data access bean + exception translation | DAO or repository classes accessing a database |
@Controller |
Stereotype | MVC controller - returns view names | Server-side rendered apps (Thymeleaf, JSP) |
@RestController |
Stereotype | @Controller + @ResponseBody - returns JSON |
REST APIs consumed by frontend or other services |
@Autowired |
Injection | Triggers automatic dependency injection | Required on setter/field injection; optional on constructor |
@Qualifier |
Injection | Specifies which bean to inject by name | When multiple beans of the same type exist |
@Primary |
Injection | Marks default bean when multiple candidates exist | On the preferred implementation of an interface |
@Configuration |
Config | Marks a class as a source of bean definitions | Classes containing @Bean methods |
@Bean |
Config | Registers method return value as a bean | Third-party objects or beans needing complex configuration |
@ComponentScan |
Config | Specifies packages to scan for components | When beans exist outside the default package |
@Value |
Properties | Injects a single property value | One-off property values; use @ConfigurationProperties for groups |
@ConfigurationProperties |
Properties | Binds a property prefix to a typed class | Groups of related configuration properties |
@RequestMapping |
Web | Maps a URL to a controller or method | On the class as a base URL prefix; use shorthand on methods |
@GetMapping |
Web | Maps HTTP GET to a method | Read operations - retrieve resources |
@PostMapping |
Web | Maps HTTP POST to a method | Create operations |
@PutMapping |
Web | Maps HTTP PUT to a method | Full replacement updates |
@DeleteMapping |
Web | Maps HTTP DELETE to a method | Delete operations |
@PathVariable |
Web | Extracts value from URL path segment | Resource identifiers in URLs like /products/{id} |
@RequestParam |
Web | Extracts value from query string | Filters, sorting, pagination |
@RequestBody |
Web | Deserialises JSON request body to Java object | POST and PUT endpoints that accept JSON |
@ResponseBody |
Web | Serialises return value to JSON response body | Applied by @RestController automatically |
@Profile |
Environment | Activates bean only for specified profile | Dev/test/prod environment switching |
@ConditionalOnProperty |
Environment | Activates bean based on property value | Feature flags, optional integrations |
@PostConstruct |
Lifecycle | Runs after dependency injection completes | Initialisation logic that needs injected dependencies |
@PreDestroy |
Lifecycle | Runs before bean is destroyed | Cleanup - closing connections, releasing resources |
Further Reading
Board Infinity Guides:
- OOP Concepts in Java
- Core Java Concepts and Syntax
- Classes and Objects in Java
- Abstraction in Java
- Abstraction vs Encapsulation in Java
- Understanding Polymorphism in Java
- Multiple Inheritance in Java - Interfaces
- Method Overloading vs Overriding in Java
- Access Modifiers in Java
- What is Composition in Java?
- Generics in Java
- Understanding Wrapper Class in Java
- Learn About Java List
- Essentials of Back-End Development: From APIs to Databases
External Resources:
- Spring Framework Official Docs - Core Annotations
- Spring Boot Official Docs - Auto-Configuration
- Baeldung - Spring Annotations Guide
Spring Framework: Core & Web Development on Coursera
This free Coursera course by Board Infinity puts every annotation covered in this guide into practice through real project work. From IoC and dependency injection to Spring MVC and validation - you'll use these annotations in context, not just read about them.
โ Certificate available ยท โ Self-paced ยท โ Beginner-friendly
Conclusion
Spring annotations are not arbitrary labels - they are precise instructions to different parts of the Spring framework. Stereotype annotations register beans. Injection annotations wire them together. Configuration annotations define how the context is assembled. Property annotations bind external configuration. Web annotations handle HTTP routing. Profile annotations control which beans are active per environment.
The key insight that ties them all together: most Spring annotations are processed at startup by the IoC container, which reads your class structure using Java reflection. Understanding that annotations are metadata - not executable code - explains why they work as they do and why the order of container initialisation matters.
Keep this guide bookmarked. The quick reference table covers the annotations you'll encounter in 95% of Spring Boot work. For the remaining edge cases - security annotations, transaction annotations, cache annotations, and testing annotations - the same mental model applies: find the framework subsystem the annotation belongs to, understand what that subsystem does, and the annotation's purpose becomes obvious. The Java foundations that make all of it work - OOP, interfaces, generics, encapsulation, and polymorphism - are covered comprehensively in Board Infinity's understanding polymorphism in Java guide and essentials of back-end development guide, which explains how the annotation-driven Spring architecture fits into the full backend stack.