Menu

Spring Boot Topic

Spring Boot Annotations

1. Introduction to Spring Boot Annotations

Spring Boot annotations are specialized form of metadata that provide declarative programming model. They simplify Java-based Spring application development by reducing boilerplate code and configuration.

Key Characteristics:

  • Compiler Instructions: Processed at compile time

  • Runtime Behavior: Influence application behavior at runtime

  • Configuration Reduction: Eliminate XML configuration needs

  • Convention Over Configuration: Provide sensible defaults

2. Core Spring Boot Annotations

2.1 Application Bootstrapping Annotations

@SpringBootApplication

Purpose: Main annotation that bootstraps Spring Boot application
Usage: Applied to main class

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

What it combines:

  • @Configuration: Marks class as configuration source

  • @EnableAutoConfiguration: Enables auto-configuration

  • @ComponentScan: Enables component scanning in current package

@SpringBootConfiguration

Purpose: Alternative to standard Spring's @Configuration
Usage: Indicates class provides application configuration

@SpringBootConfiguration
public class AppConfig {
    // Configuration methods
}

3. Stereotype Annotations (Component Model)

3.1 Core Stereotypes

@Component

Purpose: Generic stereotype for any Spring-managed component
Usage: Base annotation for all Spring components

@Component
public class DataProcessor {
    // Class implementation
}

@Service

Purpose: Specialization of @Component for service layer
Usage: Business service facade, transaction boundary

@Service
public class UserService {
    
    public User createUser(User user) {
        // Business logic
        return userRepository.save(user);
    }
}

@Repository

Purpose: Data access layer, exception translation
Usage: DAO classes, translates exceptions to Spring DataAccessException

@Repository
public class UserRepository {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    public User findByEmail(String email) {
        // Data access logic
    }
}

@Controller & @RestController

Purpose: Web controller for MVC and REST APIs

// Traditional MVC Controller
@Controller
@RequestMapping("/users")
public class UserController {
    
    @GetMapping
    public String getUsers(Model model) {
        model.addAttribute("users", userService.findAll());
        return "users";
    }
}

// REST API Controller
@RestController
@RequestMapping("/api/users")
public class UserApiController {
    
    @GetMapping
    public List<User> getUsers() {
        return userService.findAll();
    }
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
}

Key Difference: @RestController = @Controller + @ResponseBody

4. Dependency Injection Annotations

4.1 Injection Annotations

@Autowired

Purpose: Automatic dependency injection
Usage: Field, constructor, or setter injection

@Service
public class UserService {
    
    // Field Injection
    @Autowired
    private UserRepository userRepository;
    
    // Constructor Injection (Recommended)
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // Setter Injection
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

@Qualifier

Purpose: Resolves ambiguity when multiple beans of same type exist

@Service
public class PaymentService {
    
    @Autowired
    @Qualifier("creditCardProcessor")
    private PaymentProcessor paymentProcessor;
}

@Component("creditCardProcessor")
public class CreditCardProcessor implements PaymentProcessor { }

@Component("paypalProcessor")
public class PayPalProcessor implements PaymentProcessor { }

@Primary

Purpose: Defines default bean when multiple candidates exist

@Component
@Primary
public class PrimaryDataSource implements DataSource {
    // Primary implementation
}

@Component
public class SecondaryDataSource implements DataSource {
    // Secondary implementation
}

5. Configuration and Bean Annotations

5.1 Configuration Management

@Configuration

Purpose: Indicates class declares one or more @Bean methods

@Configuration
public class AppConfig {
    
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

@Bean

Purpose: Indicates method produces a bean managed by Spring

@Configuration
public class SecurityConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Qualifier("jwtTokenProvider")
    public TokenProvider jwtTokenProvider() {
        return new JwtTokenProvider();
    }
}

@Value

Purpose: Injects values from properties files, environment variables

@Service
public class EmailService {
    
    @Value("${app.email.host:smtp.gmail.com}")
    private String emailHost;
    
    @Value("${app.email.port:587}")
    private int emailPort;
    
    @Value("#{systemProperties['java.home']}")
    private String javaHome;
    
    @Value("#{T(java.lang.Math).random() * 100.0}")
    private double randomValue;
}

@ConfigurationProperties

Purpose: Binds and validates external configuration to POJO

@Component
@ConfigurationProperties(prefix = "app.database")
public class DatabaseProperties {
    private String url;
    private String username;
    private String password;
    private int poolSize = 10;
    
    // Getters and setters
}

// application.properties
app.database.url=jdbc:mysql://localhost:3306/mydb
app.database.username=admin
app.database.password=secret
app.database.pool-size=20

6. Web and REST Annotations

6.1 Request Mapping Annotations

@RequestMapping

Purpose: Maps web requests to handler methods

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
    public User saveUser(@RequestBody User user) {
        return userService.save(user);
    }
}

HTTP Method Shortcuts

@RestController
public class UserController {
    
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) { }
    
    @PostMapping("/users")
    public User createUser(@RequestBody User user) { }
    
    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) { }
    
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) { }
    
    @PatchMapping("/users/{id}")
    public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) { }
}

6.2 Parameter Binding Annotations


@PathVariable, @RequestParam, @RequestBody
 

@RestController
public class UserController {
    
    @GetMapping("/users/{userId}/orders/{orderId}")
    public Order getOrder(
        @PathVariable Long userId,
        @PathVariable Long orderId,
        @RequestParam(defaultValue = "false") boolean details,
        @RequestParam(required = false) String filter) {
        // Implementation
    }
    
    @PostMapping("/users")
    public User createUser(@RequestBody @Valid User user) {
        return userService.save(user);
    }
    
    @GetMapping("/users/search")
    public List<User> searchUsers(
        @RequestParam String keyword,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
        // Implementation
    }
}

6.3 Response Handling

@ResponseBody, @ResponseStatus, @ResponseEntity

@RestController
public class UserController {
    
    @GetMapping("/users/{id}")
    @ResponseStatus(HttpStatus.OK)
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
        User savedUser = userService.save(user);
        return ResponseEntity
            .created(URI.create("/users/" + savedUser.getId()))
            .body(savedUser);
    }
    
    @DeleteMapping("/users/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

7. Data Access Annotations

7.1 Spring Data JPA Annotations

@Entity, @Table, @Id

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "username", unique = true, nullable = false, length = 50)
    private String username;
    
    @Column(name = "email", unique = true, nullable = false)
    private String email;
    
    @Column(name = "created_at")
    private LocalDateTime createdAt;
    
    // Relationships
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
    
    @ManyToMany
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
    
    // Constructors, getters, setters
}

Repository Annotations

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // Derived queries
    Optional<User> findByEmail(String email);
    List<User> findByUsernameContainingIgnoreCase(String username);
    
    // Custom queries with @Query
    @Query("SELECT u FROM User u WHERE u.createdAt >= :startDate")
    List<User> findUsersCreatedAfter(@Param("startDate") LocalDateTime startDate);
    
    @Query(value = "SELECT * FROM users WHERE email LIKE %:domain", nativeQuery = true)
    List<User> findUsersByEmailDomain(@Param("domain") String domain);
    
    // Modifying queries
    @Modifying
    @Query("UPDATE User u SET u.lastLogin = CURRENT_TIMESTAMP WHERE u.id = :userId")
    int updateLastLogin(@Param("userId") Long userId);
}

8. Testing Annotations

8.1 Spring Boot Test Annotations

@SpringBootTest

Purpose: Full application context testing

@SpringBootTest
@ActiveProfiles("test")
class UserServiceIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @MockBean
    private EmailService emailService;
    
    @Test
    void shouldCreateUserSuccessfully() {
        // Test implementation
    }
}

@DataJpaTest, @WebMvcTest, @JsonTest

// JPA slice test
@DataJpaTest
class UserRepositoryTest {
    
    @Autowired
    private TestEntityManager entityManager;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void shouldFindByEmail() {
        // Test implementation
    }
}

// MVC slice test
@WebMvcTest(UserController.class)
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void shouldReturnUser() throws Exception {
        mockMvc.perform(get("/users/1"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.username").exists());
    }
}

9. Advanced Spring Boot Annotations

9.1 Scheduling and Async

@Scheduled, @Async

@Service
public class ScheduledTasks {
    
    @Scheduled(fixedRate = 5000) // Every 5 seconds
    public void reportCurrentTime() {
        // Task implementation
    }
    
    @Scheduled(cron = "0 0 2 * * ?") // Daily at 2 AM
    public void performNightlyCleanup() {
        // Cleanup task
    }
}

@Service
public class AsyncService {
    
    @Async
    public CompletableFuture<String> processDataAsync(String data) {
        // Async processing
        return CompletableFuture.completedFuture("Processed: " + data);
    }
}

// Enable scheduling and async
@SpringBootApplication
@EnableScheduling
@EnableAsync
public class Application { }

9.2 Caching Annotations

@Cacheable, @CacheEvict, @CachePut

@Service
public class UserService {
    
    @Cacheable(value = "users", key = "#id")
    public User findById(Long id) {
        // This method will only execute if result not in cache
        return userRepository.findById(id).orElse(null);
    }
    
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        // Always execute and update cache
        return userRepository.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        // Remove from cache
        userRepository.deleteById(id);
    }
    
    @CacheEvict(value = "users", allEntries = true)
    public void clearAllUserCache() {
        // Clear entire cache
    }
}

// Enable caching
@SpringBootApplication
@EnableCaching
public class Application { }

10. Security Annotations

10.1 Spring Security Annotations

@EnableWebSecurity, @PreAuthorize, @PostAuthorize

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(withDefaults());
        return http.build();
    }
}

@RestController
public class AdminController {
    
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/admin/users")
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    @PreAuthorize("#username == authentication.principal.username")
    @GetMapping("/users/{username}/profile")
    public UserProfile getUserProfile(@PathVariable String username) {
        return userService.findProfileByUsername(username);
    }
    
    @PostAuthorize("returnObject.owner == authentication.principal.username")
    @GetMapping("/documents/{id}")
    public Document getDocument(@PathVariable Long id) {
        return documentService.findById(id);
    }
}

11. Best Practices and Common Patterns

11.1 Annotation Organization

// Good structure for main class
@SpringBootApplication
@EnableCaching
@EnableAsync
@EnableScheduling
@ComponentScan(basePackages = "com.example")
@EntityScan(basePackages = "com.example.entity")
@EnableJpaRepositories(basePackages = "com.example.repository")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Good controller structure
@RestController
@RequestMapping("/api/v1")
@Validated
@Slf4j
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/users/{id}")
    @ResponseStatus(HttpStatus.OK)
    public User getUser(@PathVariable @Min(1) Long id) {
        return userService.findById(id);
    }
}

11.2 Common Pitfalls to Avoid

  1. Overusing @Autowired on fields - Prefer constructor injection

  2. Missing @Transactional on service methods that modify data

  3. Incorrect @RequestMapping method types

  4. Not validating @RequestBody with @Valid

  5. Forgetting @ResponseBody in traditional @Controller

This comprehensive guide covers the essential Spring Boot annotations with practical examples. Each annotation serves specific purposes in building robust, maintainable Spring Boot applications.

 

 

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?