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 @Component annotation

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.

Java - All 4 Stereotype Annotations in a Typical Spring App
// @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(); }
}
๐Ÿ”
Why @Service Exists When @Component Does the Same Thing

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.

Java - @Autowired, @Qualifier and @Primary
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
}
}
โš ๏ธ
Skip @Autowired on Constructor - Spring Injects Automatically

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.

Java - @Configuration, @Bean and @ComponentScan
// @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();
}
}
๐Ÿ“Œ
@Bean vs @Component - When to Use Each

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.

Java - @Value vs @ConfigurationProperties
// 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.

Java - Web Layer Annotations in a REST Controller
@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);
}
}
๐Ÿ’ก
Use @ResponseStatus for Simple Status Codes - ResponseEntity for Full Control

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.

Java - @Profile for Environment-Based Bean Switching
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 Can Also Exclude Beans with "not" Syntax

@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:

External Resources:

๐Ÿš€ Go Deeper - Use Every Annotation in a Real Project

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.

Module 1
Spring Core & Dependency Injection @Component, @Service, @Repository, @Autowired, @Qualifier, @Primary - all the bean and injection annotations in practice
Module 2
Configuring Spring Applications @Configuration, @Bean, @ComponentScan, @Value, @ConfigurationProperties, @Profile - configuration and property annotations hands-on
Module 3
Building Web Apps with Spring MVC @RestController, @GetMapping, @PostMapping, @PathVariable, @RequestBody - every web annotation used in a real REST API build
Module 4
Validation & Error Handling @Valid, @NotBlank, @Email, @Size, @RestControllerAdvice, @ExceptionHandler - validation and error annotations in full
Start Learning on Coursera โ†’

โœ“ 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.

Programming Language Java Spring Spring Annotations Advance Java