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
-
Overusing
@Autowiredon fields - Prefer constructor injection -
Missing
@Transactionalon service methods that modify data -
Incorrect
@RequestMappingmethod types -
Not validating
@RequestBodywith@Valid -
Forgetting
@ResponseBodyin 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!