Course topics

By WebNest Studio

Spring Boot Tutorial

Spring Boot Annotations Reference

Spring Boot code is built from annotations: they declare components, inject dependencies, map URLs, bind configuration, manage transactions and configure tests. Knowing what each common annotation does — and which package it lives in — makes reading any Spring Boot project much easier.

This lesson is a categorised reference of the annotations you will use most, each with a short explanation and a combined example that uses them together.

Application and Configuration

These define the application and its beans:

  • @SpringBootApplication — combines @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan.
  • @Configuration — a class that declares beans with @Bean methods.
  • @Bean — registers the method's return value as a Spring bean.
  • @ComponentScan — packages to scan for components.
  • @Import — imports other configuration classes.
  • @Profile("dev") — only active in the given profile.
  • @Conditional... — @ConditionalOnProperty, @ConditionalOnMissingBean, @ConditionalOnClass for conditional beans.
  • @ConfigurationProperties(prefix = "app") and @EnableConfigurationProperties — type-safe configuration binding.
  • @Value("${app.name}") — inject a single property value.

Stereotypes and Dependency Injection

Stereotypes mark classes for component scanning and describe their role:

  • @Component — a generic Spring-managed component.
  • @Service — business logic layer.
  • @Repository — persistence layer; also translates database exceptions to Spring's DataAccessException.
  • @Controller / @RestController — web layer; RestController adds @ResponseBody to every method.
  • @Autowired — inject a dependency (optional on a single constructor, which is the recommended style).
  • @Qualifier("name") and @Primary — choose between several beans of the same type.
  • @Scope("prototype"), @Lazy, @PostConstruct, @PreDestroy — scope, lazy creation and lifecycle callbacks.

Web (Spring MVC)

Request handling annotations:

  • @RequestMapping, @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping.
  • @PathVariable, @RequestParam, @RequestBody, @RequestHeader, @CookieValue, @ModelAttribute.
  • @ResponseStatus — fixed HTTP status for a method or exception class.
  • @RestControllerAdvice / @ControllerAdvice and @ExceptionHandler — global error handling.
  • @CrossOrigin — per-controller CORS.
  • @Valid / @Validated — trigger Bean Validation.

Data, Transactions and Other Features

Persistence and cross-cutting annotations:

  • @Entity, @Table, @Id, @GeneratedValue, @Column, @OneToMany, @ManyToOne — JPA mapping.
  • @Query, @Modifying, @EntityGraph — Spring Data repository methods.
  • @Transactional — transaction boundaries.
  • @EnableCaching/@Cacheable, @EnableAsync/@Async, @EnableScheduling/@Scheduled, @EnableResilientMethods/@Retryable.
  • @EventListener, @TransactionalEventListener — application events.
  • @Aspect, @Before, @Around... — AOP.
  • @EnableMethodSecurity, @PreAuthorize — method security.

Testing

Test annotations from Spring Boot and Spring Test:

  • @SpringBootTest — full application context.
  • @WebMvcTest, @DataJpaTest, @RestClientTest, @JsonTest — slice tests.
  • @MockitoBean, @MockitoSpyBean — replace beans with mocks or spies.
  • @AutoConfigureMockMvc, @AutoConfigureRestTestClient — test clients.
  • @ServiceConnection, @Testcontainers, @Container — Testcontainers integration.
  • @TestConfiguration, @ActiveProfiles, @DynamicPropertySource, @WithMockUser.

Examples

One small feature using the most common annotations together

Java
@SpringBootApplication
@EnableConfigurationProperties(ShopProperties.class)
@EnableCaching
public class ShopApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShopApplication.class, args);
    }
}

@ConfigurationProperties(prefix = "shop")
record ShopProperties(String currency, int maxCartItems) {}

@Entity
class Product {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id;
    @Column(nullable = false) String name;
    BigDecimal price;
}

@Repository
interface ProductRepository extends JpaRepository<Product, Long> {
    @Query("select p from Product p where p.price <= :max order by p.price")
    List<Product> cheaperThan(BigDecimal max);
}

@Service
class ProductService {
    private final ProductRepository repo;
    private final ShopProperties props;

    ProductService(ProductRepository repo, ShopProperties props) {   // constructor injection
        this.repo = repo;
        this.props = props;
    }

    @Cacheable("cheapProducts")
    @Transactional(readOnly = true)
    public List<Product> cheaperThan(BigDecimal max) {
        return repo.cheaperThan(max);
    }
}

@RestController
@RequestMapping("/api/products")
class ProductController {
    private final ProductService service;

    ProductController(ProductService service) {
        this.service = service;
    }

    @GetMapping
    List<Product> cheap(@RequestParam(defaultValue = "1000") BigDecimal max) {
        return service.cheaperThan(max);
    }
}

@RestControllerAdvice
class ApiErrors {
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    ProblemDetail badRequest(IllegalArgumentException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage());
    }
}
Output
GET /api/products?max=800
[{"id":3,"name":"Cap","price":499.00},{"id":2,"name":"Tee","price":799.00}]
(second identical request is served from the "cheapProducts" cache)

Choosing between beans with @Primary and @Qualifier

Java
interface PaymentGateway { String pay(BigDecimal amount); }

@Component
@Primary
class RazorpayGateway implements PaymentGateway {
    public String pay(BigDecimal amount) { return "razorpay:" + amount; }
}

@Component("stripeGateway")
class StripeGateway implements PaymentGateway {
    public String pay(BigDecimal amount) { return "stripe:" + amount; }
}

@Service
class CheckoutService {
    private final PaymentGateway defaultGateway;
    private final PaymentGateway internationalGateway;

    CheckoutService(PaymentGateway defaultGateway,
                    @Qualifier("stripeGateway") PaymentGateway internationalGateway) {
        this.defaultGateway = defaultGateway;             // @Primary wins
        this.internationalGateway = internationalGateway;
    }
}
Output
defaultGateway.pay(100)       -> razorpay:100
internationalGateway.pay(100) -> stripe:100

Common Mistakes

  • Using field injection with @Autowired everywhere instead of constructor injection.
  • Putting @Transactional or @Cacheable on private methods, where proxies cannot apply them.
  • Confusing @Controller (returns view names) with @RestController (returns response bodies).
  • Adding @EnableWebMvc or @EnableAutoConfiguration again in a Boot app that already has @SpringBootApplication.
  • Mixing javax.* annotations from old tutorials with jakarta.* in Spring Boot 3/4 projects.

Key Points to Remember

  • @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan.
  • Stereotypes (@Component, @Service, @Repository, @RestController) mark beans by layer.
  • Web annotations map requests and bind data; @RestControllerAdvice handles errors globally.
  • Feature annotations (@Transactional, @Cacheable, @Async, @Scheduled) work through proxies.
  • Test annotations provide full-context, slice, mock and container-based tests.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

Use your local JDK or project IDE for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.