SPRING BOOT & JPA#04 • Core Framework Annotations

Spring Boot Important Annotations: The Complete Practical Developer Guide.

A comprehensive, categorized handbook covering all essential Spring Boot and Spring Data JPA annotations. Clear explanations, real-world code snippets, and production tips without unnecessary buzzwords.

By Shyam KumarAug 18, 202612 min read40+ Annotations Detailed
#SpringBoot#Java#JPA#Hibernate

Why Are Annotations Fundamental to Spring Boot?

Spring Boot replaced heavy XML configurations with declarative Java annotations. Under the hood, annotations act as metadata for the Spring IoC Container and Hibernate bytecode enhancers, instructing the framework on how to construct beans, configure web routes, and map database tables.

Category 01

Core Bootstrap & Application Context

@SpringBootApplication

Placed on the main class to bootstrap a Spring Boot application. It is a meta-annotation combining three essential annotations:

  • @Configuration: Tags the class as a source of bean definitions.
  • @EnableAutoConfiguration: Automatically configures beans based on classpath dependencies (Tomcat, Jackson, DataSource).
  • @ComponentScan: Scans the current package and its sub-packages for @Component, @Service, @Repository, and @Controller classes.
MySpringBootApplication.java(Java)
package com.example.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MySpringBootApplication {
    public static void main(String[] args) {
        // Starts the Spring ApplicationContext and embedded web server
        SpringApplication.run(MySpringBootApplication.class, args);
    }
}

@Configuration

Marks a class as a configuration source. Methods inside this class use @Bean to instantiate and register beans in the Spring IoC container.

AppConfig.java(Java)
@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@ComponentScan

Specifies custom package locations to scan if your dependencies or shared components live outside the main application package.

CustomScanApplication.java(Java)
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.app", "com.external.shared"})
public class CustomScanApplication {
    public static void main(String[] args) {
        SpringApplication.run(CustomScanApplication.class, args);
    }
}

@Bean and @Scope

@Bean: Method-level annotation used to register third-party library objects (such as Jackson ObjectMapper or an S3 client) in the Spring container.
@Scope: Defines bean lifecycle. The default scope is singleton (one instance per Spring Context). Other scopes include prototype (new instance on every injection), request (per HTTP request), and session (per user session).

AuditConfig.java(Java)
@Configuration
public class AuditConfig {

    @Bean
    @Scope("prototype") // New instance created every time someone requests this bean
    public AuditLogger auditLogger() {
        return new AuditLogger(UUID.randomUUID().toString());
    }

    @Bean
    @Scope("singleton") // Default: Only one instance lives in the entire JVM
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

@EnableAutoConfiguration

Tells Spring Boot to automatically configure sensible defaults based on jars found on the classpath. Unwanted configurations can be disabled using the exclude parameter.

ExcludeApplication.java(Java)
// Disable default DataSource auto-config if we don't need a DB right away
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class ExcludeApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExcludeApplication.class, args);
    }
}
Category 02

Stereotypes & Dependency Injection

@Component, @Service, @Repository

Core stereotype annotations that mark classes for automatic detection during component scanning:

@Component

Generic Spring-managed component (utilities, helpers, background workers).

@Service

Business logic layer. Clarifies intent for transactional service methods.

@Repository

Data access layer (DAO). Automatically translates database SQLExceptions into Spring's unified DataAccessException hierarchy.

OrderLayersExample.java(Java)
@Repository
public class OrderRepositoryImpl implements OrderRepository {
    // Database access operations
}

@Service
public class OrderService {
    // Core business workflows, transaction demarcation
}

@Component
public class CurrencyConverterHelper {
    // General utility component
}

@Autowired and @Qualifier

@Autowired: Tells Spring to inject dependencies automatically. In modern Spring Boot, constructor injection with final fields is the recommended practice over field injection.
@Qualifier: Resolves ambiguity when multiple beans implement the same interface, preventing NoUniqueBeanDefinitionException.

PaymentService.java(Java)
public interface PaymentGateway {
    void pay(double amount);
}

@Component("stripeGateway")
public class StripeGateway implements PaymentGateway { ... }

@Component("paypalGateway")
public class PaypalGateway implements PaymentGateway { ... }

@Service
public class OrderProcessingService {

    private final PaymentGateway paymentGateway;

    // @Autowired on single constructor is optional in modern Spring, but explicit is good
    public OrderProcessingService(@Qualifier("stripeGateway") PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway; // Resolves ambiguity cleanly
    }
}
Category 03

Web & REST API Mappings

@RestController vs @Controller & @ResponseBody

The core formula behind modern Spring REST endpoints:@RestController = @Controller + @ResponseBodyWith @Controller, return strings are treated as HTML template names (Thymeleaf/JSP). With @RestController or @ResponseBody, returned objects are serialized directly into the HTTP response body as JSON via Jackson.

UserController.java(Java)
@RestController
@RequestMapping("/api/v1/users")
public class UserRestController {

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        return new UserResponse(id, "Shyam", "shyam@example.com"); 
        // Serialized automatically into JSON payload
    }
}

HTTP Method Mappings & @RequestBody

Standardized mapping shortcuts for routing HTTP requests and extracting payloads:

  • @RequestMapping: Sets a base URL path at the class level.
  • @GetMapping: Handles HTTP GET requests (reads/fetches data).
  • @PostMapping: Handles HTTP POST requests (creates resources).
  • @PutMapping: Handles HTTP PUT requests (replaces/updates resources).
  • @RequestBody: Deserializes the incoming JSON request body into a typed Java DTO.
ProductEndpointController.java(Java)
@RestController
@RequestMapping("/api/products")
public class ProductEndpointController {

    // GET /api/products/42
    @GetMapping("/{id}")
    public ProductDTO getProduct(@PathVariable Long id) { ... }

    // POST /api/products (Takes JSON body and converts to CreateProductRequest DTO)
    @PostMapping
    public ResponseEntity<ProductDTO> createProduct(@RequestBody CreateProductRequest request) {
        ProductDTO saved = productService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    }

    // PUT /api/products/42
    @PutMapping("/{id}")
    public ResponseEntity<ProductDTO> updateProduct(
        @PathVariable Long id, 
        @RequestBody UpdateProductRequest request
    ) {
        return ResponseEntity.ok(productService.update(id, request));
    }
}

@EnableWebMvc

⚠️ Production Warning: Avoid manually adding @EnableWebMvc in Spring Boot applications if you want to keep Spring Boot's automatic Web MVC configuration. This annotation switches off Boot's default auto-configuration for static resources and JSON message converters, giving you a completely blank setup.

Use this only when configuring pure Spring MVC without Spring Boot.

Category 04

Configuration & Environment Properties

@Value vs @ConfigurationProperties

@Value: Injects individual property values from application.properties with SpEL expression support.
@ConfigurationProperties: Binds hierarchical, type-safe configuration groups (nested objects, maps, lists). Recommended for production configs.
@PropertySource: Loads custom configuration files outside the default application.properties file.

PaymentGatewayConfig.java(Java)
// 1. Using @Value for simple single values
@Component
public class NotificationService {
    @Value("${app.mail.sender:noreply@company.com}")
    private String senderEmail;
}

// 2. Type-Safe Configuration Properties (Best Practice for complex configs)
@Configuration
@ConfigurationProperties(prefix = "payment.gateway")
public class PaymentGatewayConfig {
    private String apiKey;
    private int timeoutSeconds;
    private boolean sandboxMode;

    // getters and setters...
}

@ConditionalOnProperty

Used for feature flags and conditional bean instantiation. The bean is created only when the specified property key matches the expected value in application.properties.

MockSmsService.java(Java)
@Service
@ConditionalOnProperty(name = "feature.mock-sms.enabled", havingValue = "true", matchIfMissing = false)
public class MockSmsService implements SmsService {
    @Override
    public void send(String phone, String message) {
        System.out.println("Mock SMS logged: " + message);
    }
}
Category 05

JPA Entities & Lifecycle Callbacks

Entity Core Mapping

@Entity: Marks a class as a JPA persistent entity.
@Table: Specifies table name and database indexes.
@Id: Marks the primary key field.
@GeneratedValue: Configures primary key generation strategies (IDENTITY, SEQUENCE, UUID).
@Column: Defines column names, nullable flags, lengths, and constraints.
@Transient: Tells JPA not to persist this field in the database table (in-memory only).
Customer.java(Java)
@Entity
@Table(name = "customers")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "email_address", nullable = false, unique = true, length = 150)
    private String email;

    @Column(name = "account_balance", precision = 12, scale = 2)
    private BigDecimal balance;

    // Field will NOT be saved to the database table!
    @Transient
    private String temporaryAuthToken;
}

@EntityListeners & Lifecycle Callbacks

Automated lifecycle callback hooks triggered during entity operations (insert, update, delete):

  • @PrePersist: Runs before an insert SQL query executes (e.g., populating createdAt timestamps).
  • @PostPersist: Runs immediately after a database row is inserted.
  • @PreUpdate: Runs before an existing row is updated in the database (e.g., updating updatedAt timestamps).
  • @PostUpdate: Runs after an update commit completes.
  • @PreRemove: Runs before a row is deleted.
  • @PostRemove: Runs after a row is successfully deleted.
  • @EntityListeners: Delegates entity lifecycle callbacks to reusable listener classes (such as AuditingEntityListener.class).
BaseAuditEntity.java(Java)
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseAuditEntity {

    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    public void onBeforeInsert() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    public void onBeforeUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    @PostRemove
    public void onAfterDelete() {
        System.out.println("Entity record removed from database.");
    }
}
Category 06

JPA Table Relationships: @OneToOne, @OneToMany, @ManyToMany

Mapping Relational Foreign Keys in Java

Primary annotations for mapping relational database foreign keys:

1. @OneToOne

Maps a single relationship between two tables (e.g., a User and their UserProfile). @JoinColumn defines the foreign key column on the owning entity.

2. @OneToMany & @ManyToOne

Bidirectional parent-child relationship (e.g., an Order and its OrderItems). Best practice is to make the child entity (OrderItem) the owning side with @ManyToOne, and use mappedBy on the parent (Order) side.

3. @ManyToMany

Maps relationships between entities where both sides can reference multiple records (e.g., Students and Courses). Uses @JoinTable to define the junction table.

OrderAndItemEntities.java(Java)
@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // One order has many items. 'order' is the field name inside OrderItem
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
}

@Entity
@Table(name = "order_items")
public class OrderItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // Owning side holding the foreign key column 'order_id'
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private Order order;
}
Category 07

Queries & Transactions: @Query, @Param, @Transactional

@Query and @Param

Spring Data JPA method naming conventions handle simple queries, but @Query is used for complex joins or updates. JPQL (Java Persistence Query Language) queries target Java entity classes and fields, while nativeQuery = true executes raw database SQL.@Param binds method arguments to named parameters in the query (:status).

OrderRepository.java(Java)
public interface OrderRepository extends JpaRepository<Order, Long> {

    // JPQL Query targeting Java Entity fields
    @Query("SELECT o FROM Order o WHERE o.customer.id = :customerId AND o.status = :status")
    List<Order> findActiveCustomerOrders(
        @Param("customerId") Long customerId, 
        @Param("status") OrderStatus status
    );

    // Native Raw SQL Query with database-specific syntax
    @Query(value = "SELECT * FROM orders WHERE created_at < NOW() - INTERVAL '30 days'", nativeQuery = true)
    List<Order> findStaleOrders();
}

@Transactional

Guarantees atomic execution (ACID properties) for database operations. If an unhandled exception occurs during method execution, all database changes are rolled back automatically.

💡 Production Rule: By default, Spring only rolls back on unchecked exceptions (RuntimeException). To roll back on checked exceptions as well, always specify @Transactional(rollbackFor = Exception.class).
MoneyTransferService.java(Java)
@Service
public class BankTransferService {

    private final AccountRepository accountRepo;

    @Transactional(rollbackFor = Exception.class)
    public void transferMoney(Long fromAccId, Long toAccId, BigDecimal amount) {
        // Step 1: Deduct from source
        accountRepo.deductBalance(fromAccId, amount);

        // Simulated crash or failure
        if (amount.compareTo(new BigDecimal("1000000")) > 0) {
            throw new IllegalStateException("Transfer limit exceeded. Aborting!");
        }

        // Step 2: Credit to destination
        accountRepo.creditBalance(toAccId, amount);
        
        // If an exception occurs, Step 1 is rolled back completely!
    }
}

Summary: Quick Decision Matrix

Task / ScenarioRecommended AnnotationKey Advantage
REST API Controller@RestControllerCombines @Controller and @ResponseBody into one clean declaration.
Inject 3rd party class as Bean@Configuration + @BeanAllows wrapping un-annotated library classes (like ObjectMapper).
Multiple Beans of Same Interface@QualifierEliminates ambiguity when injecting beans.
Atomic multi-step DB changes@TransactionalGuarantees all-or-nothing rollback on exceptions.
Temporary non-persisted DB field@TransientPrevents Hibernate from creating database columns for memory-only fields.

Did you find this Spring Boot Annotations handbook helpful?

Share this reference with fellow Java developers building clean, enterprise-ready Spring Boot services!

Bookmark: Save for your daily coding reference
Share: With your backend and Java teams
Back to All Articles in The Tech Corner
Have a Question? Contact Me