@brookmind/ai-toolkit 1.1.4 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1519 @@
1
+ ---
2
+ name: spring-boot-development
3
+ description: Comprehensive Spring Boot development skill covering auto-configuration, dependency injection, REST APIs, Spring Data, security, and enterprise Java applications
4
+ category: backend
5
+ tags: [spring-boot, java, rest-api, spring-data, jpa, security, microservices, enterprise]
6
+ version: 1.0.0
7
+ context7_library: /spring-projects/spring-boot
8
+ context7_trust_score: 7.5
9
+ ---
10
+
11
+ # Spring Boot Development Skill
12
+
13
+ This skill provides comprehensive guidance for building modern Spring Boot applications using auto-configuration, dependency injection, REST APIs, Spring Data, Spring Security, and enterprise Java patterns based on official Spring Boot documentation.
14
+
15
+ ## When to Use This Skill
16
+
17
+ Use this skill when:
18
+ - Building enterprise REST APIs and microservices
19
+ - Creating web applications with Spring MVC
20
+ - Developing data-driven applications with JPA and databases
21
+ - Implementing authentication and authorization with Spring Security
22
+ - Building production-ready applications with actuator and monitoring
23
+ - Creating scalable backend services with Spring Boot
24
+ - Migrating from traditional Spring to Spring Boot
25
+ - Developing cloud-native applications
26
+ - Building event-driven systems with messaging
27
+ - Creating batch processing applications
28
+
29
+ ## Core Concepts
30
+
31
+ ### Auto-Configuration
32
+
33
+ Spring Boot automatically configures your application based on the dependencies you have added to the project. This reduces boilerplate configuration significantly.
34
+
35
+ **How Auto-Configuration Works:**
36
+ ```java
37
+ @SpringBootApplication
38
+ public class MyApplication {
39
+ public static void main(String[] args) {
40
+ SpringApplication.run(MyApplication.class, args);
41
+ }
42
+ }
43
+ ```
44
+
45
+ The `@SpringBootApplication` annotation is a combination of:
46
+ - `@Configuration`: Tags the class as a source of bean definitions
47
+ - `@EnableAutoConfiguration`: Enables Spring Boot's auto-configuration mechanism
48
+ - `@ComponentScan`: Enables component scanning in the current package and sub-packages
49
+
50
+ **Conditional Auto-Configuration:**
51
+ ```java
52
+ @Configuration
53
+ @ConditionalOnClass(DataSource.class)
54
+ @ConditionalOnProperty(name = "spring.datasource.url")
55
+ public class DataSourceAutoConfiguration {
56
+
57
+ @Bean
58
+ @ConditionalOnMissingBean
59
+ public DataSource dataSource() {
60
+ return DataSourceBuilder.create().build();
61
+ }
62
+ }
63
+ ```
64
+
65
+ **Customizing Auto-Configuration:**
66
+ ```java
67
+ // Exclude specific auto-configurations
68
+ @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
69
+ public class MyApplication {
70
+ // ...
71
+ }
72
+
73
+ // Or in application.properties
74
+ // spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
75
+ ```
76
+
77
+ ### Dependency Injection
78
+
79
+ Spring's IoC (Inversion of Control) container manages object creation and dependency injection.
80
+
81
+ **Constructor Injection (Recommended):**
82
+ ```java
83
+ @Service
84
+ public class UserService {
85
+
86
+ private final UserRepository userRepository;
87
+ private final EmailService emailService;
88
+
89
+ // Constructor injection - recommended approach
90
+ public UserService(UserRepository userRepository, EmailService emailService) {
91
+ this.userRepository = userRepository;
92
+ this.emailService = emailService;
93
+ }
94
+
95
+ public User createUser(User user) {
96
+ User saved = userRepository.save(user);
97
+ emailService.sendWelcomeEmail(saved);
98
+ return saved;
99
+ }
100
+ }
101
+ ```
102
+
103
+ **Field Injection (Not Recommended):**
104
+ ```java
105
+ @Service
106
+ public class UserService {
107
+
108
+ @Autowired // Avoid field injection
109
+ private UserRepository userRepository;
110
+
111
+ // Difficult to test and creates tight coupling
112
+ }
113
+ ```
114
+
115
+ **Setter Injection (Optional Dependencies):**
116
+ ```java
117
+ @Service
118
+ public class UserService {
119
+
120
+ private UserRepository userRepository;
121
+ private EmailService emailService;
122
+
123
+ @Autowired
124
+ public void setUserRepository(UserRepository userRepository) {
125
+ this.userRepository = userRepository;
126
+ }
127
+
128
+ @Autowired(required = false)
129
+ public void setEmailService(EmailService emailService) {
130
+ this.emailService = emailService;
131
+ }
132
+ }
133
+ ```
134
+
135
+ **Component Stereotypes:**
136
+ ```java
137
+ @Component // Generic component
138
+ public class MyComponent { }
139
+
140
+ @Service // Business logic layer
141
+ public class MyService { }
142
+
143
+ @Repository // Data access layer
144
+ public class MyRepository { }
145
+
146
+ @Controller // Presentation layer (web)
147
+ public class MyController { }
148
+
149
+ @RestController // REST API controller
150
+ public class MyRestController { }
151
+ ```
152
+
153
+ ### Spring Web (REST APIs)
154
+
155
+ Build RESTful web services with Spring MVC annotations.
156
+
157
+ **Basic REST Controller:**
158
+ ```java
159
+ @RestController
160
+ @RequestMapping("/api/users")
161
+ public class UserController {
162
+
163
+ private final UserService userService;
164
+
165
+ public UserController(UserService userService) {
166
+ this.userService = userService;
167
+ }
168
+
169
+ @GetMapping
170
+ public List<User> getAllUsers() {
171
+ return userService.findAll();
172
+ }
173
+
174
+ @GetMapping("/{id}")
175
+ public ResponseEntity<User> getUserById(@PathVariable Long id) {
176
+ return userService.findById(id)
177
+ .map(ResponseEntity::ok)
178
+ .orElse(ResponseEntity.notFound().build());
179
+ }
180
+
181
+ @PostMapping
182
+ public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
183
+ User created = userService.save(user);
184
+ URI location = ServletUriComponentsBuilder
185
+ .fromCurrentRequest()
186
+ .path("/{id}")
187
+ .buildAndExpand(created.getId())
188
+ .toUri();
189
+ return ResponseEntity.created(location).body(created);
190
+ }
191
+
192
+ @PutMapping("/{id}")
193
+ public ResponseEntity<User> updateUser(@PathVariable Long id,
194
+ @RequestBody @Valid User user) {
195
+ return userService.update(id, user)
196
+ .map(ResponseEntity::ok)
197
+ .orElse(ResponseEntity.notFound().build());
198
+ }
199
+
200
+ @DeleteMapping("/{id}")
201
+ public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
202
+ if (userService.delete(id)) {
203
+ return ResponseEntity.noContent().build();
204
+ }
205
+ return ResponseEntity.notFound().build();
206
+ }
207
+ }
208
+ ```
209
+
210
+ **Request Mapping Variations:**
211
+ ```java
212
+ @RestController
213
+ @RequestMapping("/api/products")
214
+ public class ProductController {
215
+
216
+ // Query parameters
217
+ @GetMapping("/search")
218
+ public List<Product> search(@RequestParam String name,
219
+ @RequestParam(required = false) String category) {
220
+ return productService.search(name, category);
221
+ }
222
+
223
+ // Multiple path variables
224
+ @GetMapping("/categories/{categoryId}/products/{productId}")
225
+ public Product getProductInCategory(@PathVariable Long categoryId,
226
+ @PathVariable Long productId) {
227
+ return productService.findInCategory(categoryId, productId);
228
+ }
229
+
230
+ // Request headers
231
+ @GetMapping("/{id}")
232
+ public Product getProduct(@PathVariable Long id,
233
+ @RequestHeader("Accept-Language") String language) {
234
+ return productService.find(id, language);
235
+ }
236
+
237
+ // Matrix variables
238
+ @GetMapping("/{id}")
239
+ public Product getProductWithMatrix(@PathVariable Long id,
240
+ @MatrixVariable Map<String, String> filters) {
241
+ return productService.findWithFilters(id, filters);
242
+ }
243
+ }
244
+ ```
245
+
246
+ **Response Handling:**
247
+ ```java
248
+ @RestController
249
+ @RequestMapping("/api/orders")
250
+ public class OrderController {
251
+
252
+ // Return different status codes
253
+ @PostMapping
254
+ public ResponseEntity<Order> createOrder(@RequestBody Order order) {
255
+ Order created = orderService.create(order);
256
+ return ResponseEntity.status(HttpStatus.CREATED).body(created);
257
+ }
258
+
259
+ // Custom headers
260
+ @GetMapping("/{id}")
261
+ public ResponseEntity<Order> getOrder(@PathVariable Long id) {
262
+ Order order = orderService.findById(id);
263
+ return ResponseEntity.ok()
264
+ .header("X-Order-Version", order.getVersion().toString())
265
+ .body(order);
266
+ }
267
+
268
+ // No content response
269
+ @DeleteMapping("/{id}")
270
+ public ResponseEntity<Void> deleteOrder(@PathVariable Long id) {
271
+ orderService.delete(id);
272
+ return ResponseEntity.noContent().build();
273
+ }
274
+ }
275
+ ```
276
+
277
+ ### Spring Data JPA
278
+
279
+ Spring Data JPA provides repository abstractions for database access.
280
+
281
+ **Entity Definition:**
282
+ ```java
283
+ @Entity
284
+ @Table(name = "users")
285
+ public class User {
286
+
287
+ @Id
288
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
289
+ private Long id;
290
+
291
+ @Column(nullable = false, unique = true)
292
+ private String email;
293
+
294
+ @Column(nullable = false)
295
+ private String name;
296
+
297
+ @Column(name = "created_at", nullable = false, updatable = false)
298
+ private LocalDateTime createdAt;
299
+
300
+ @Column(name = "updated_at")
301
+ private LocalDateTime updatedAt;
302
+
303
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
304
+ private List<Order> orders = new ArrayList<>();
305
+
306
+ @ManyToOne(fetch = FetchType.LAZY)
307
+ @JoinColumn(name = "department_id")
308
+ private Department department;
309
+
310
+ @PrePersist
311
+ protected void onCreate() {
312
+ createdAt = LocalDateTime.now();
313
+ updatedAt = LocalDateTime.now();
314
+ }
315
+
316
+ @PreUpdate
317
+ protected void onUpdate() {
318
+ updatedAt = LocalDateTime.now();
319
+ }
320
+
321
+ // Getters and setters
322
+ }
323
+ ```
324
+
325
+ **Repository Interface:**
326
+ ```java
327
+ @Repository
328
+ public interface UserRepository extends JpaRepository<User, Long> {
329
+
330
+ // Query method - Spring Data generates implementation
331
+ Optional<User> findByEmail(String email);
332
+
333
+ List<User> findByNameContaining(String name);
334
+
335
+ List<User> findByDepartmentId(Long departmentId);
336
+
337
+ // Custom JPQL query
338
+ @Query("SELECT u FROM User u WHERE u.email = ?1")
339
+ Optional<User> findByEmailQuery(String email);
340
+
341
+ // Named parameters
342
+ @Query("SELECT u FROM User u WHERE u.name LIKE %:name% AND u.department.id = :deptId")
343
+ List<User> searchByNameAndDepartment(@Param("name") String name,
344
+ @Param("deptId") Long deptId);
345
+
346
+ // Native SQL query
347
+ @Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)
348
+ Optional<User> findByEmailNative(String email);
349
+
350
+ // Modifying query
351
+ @Modifying
352
+ @Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
353
+ int updateUserName(@Param("id") Long id, @Param("name") String name);
354
+
355
+ // Pagination and sorting
356
+ Page<User> findByDepartmentId(Long departmentId, Pageable pageable);
357
+
358
+ List<User> findByNameContaining(String name, Sort sort);
359
+ }
360
+ ```
361
+
362
+ **Repository Usage:**
363
+ ```java
364
+ @Service
365
+ public class UserService {
366
+
367
+ private final UserRepository userRepository;
368
+
369
+ public UserService(UserRepository userRepository) {
370
+ this.userRepository = userRepository;
371
+ }
372
+
373
+ public Optional<User> findById(Long id) {
374
+ return userRepository.findById(id);
375
+ }
376
+
377
+ public User save(User user) {
378
+ return userRepository.save(user);
379
+ }
380
+
381
+ public List<User> findAll() {
382
+ return userRepository.findAll();
383
+ }
384
+
385
+ public Page<User> findAll(int page, int size) {
386
+ Pageable pageable = PageRequest.of(page, size, Sort.by("name"));
387
+ return userRepository.findAll(pageable);
388
+ }
389
+
390
+ public boolean delete(Long id) {
391
+ if (userRepository.existsById(id)) {
392
+ userRepository.deleteById(id);
393
+ return true;
394
+ }
395
+ return false;
396
+ }
397
+ }
398
+ ```
399
+
400
+ **Relationships:**
401
+ ```java
402
+ // One-to-Many
403
+ @Entity
404
+ public class Order {
405
+ @Id
406
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
407
+ private Long id;
408
+
409
+ @ManyToOne(fetch = FetchType.LAZY)
410
+ @JoinColumn(name = "user_id")
411
+ private User user;
412
+
413
+ @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
414
+ private List<OrderItem> items = new ArrayList<>();
415
+ }
416
+
417
+ // Many-to-Many
418
+ @Entity
419
+ public class Student {
420
+ @Id
421
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
422
+ private Long id;
423
+
424
+ @ManyToMany
425
+ @JoinTable(
426
+ name = "student_course",
427
+ joinColumns = @JoinColumn(name = "student_id"),
428
+ inverseJoinColumns = @JoinColumn(name = "course_id")
429
+ )
430
+ private Set<Course> courses = new HashSet<>();
431
+ }
432
+ ```
433
+
434
+ ### Configuration
435
+
436
+ Spring Boot uses `application.properties` or `application.yml` for configuration.
437
+
438
+ **Application Properties:**
439
+ ```properties
440
+ # Server configuration
441
+ server.port=8080
442
+ server.servlet.context-path=/api
443
+
444
+ # Database configuration
445
+ spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
446
+ spring.datasource.username=user
447
+ spring.datasource.password=password
448
+ spring.datasource.driver-class-name=org.postgresql.Driver
449
+
450
+ # JPA configuration
451
+ spring.jpa.hibernate.ddl-auto=validate
452
+ spring.jpa.show-sql=true
453
+ spring.jpa.properties.hibernate.format_sql=true
454
+ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
455
+
456
+ # Logging
457
+ logging.level.root=INFO
458
+ logging.level.com.example=DEBUG
459
+ logging.level.org.springframework.web=DEBUG
460
+ logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
461
+
462
+ # Custom properties
463
+ app.name=My Application
464
+ app.version=1.0.0
465
+ ```
466
+
467
+ **Application YAML:**
468
+ ```yaml
469
+ server:
470
+ port: 8080
471
+ servlet:
472
+ context-path: /api
473
+
474
+ spring:
475
+ datasource:
476
+ url: jdbc:postgresql://localhost:5432/mydb
477
+ username: user
478
+ password: password
479
+ driver-class-name: org.postgresql.Driver
480
+
481
+ jpa:
482
+ hibernate:
483
+ ddl-auto: validate
484
+ show-sql: true
485
+ properties:
486
+ hibernate:
487
+ format_sql: true
488
+ dialect: org.hibernate.dialect.PostgreSQLDialect
489
+
490
+ logging:
491
+ level:
492
+ root: INFO
493
+ com.example: DEBUG
494
+ org.springframework.web: DEBUG
495
+
496
+ app:
497
+ name: My Application
498
+ version: 1.0.0
499
+ ```
500
+
501
+ **Configuration Properties Class:**
502
+ ```java
503
+ @Configuration
504
+ @ConfigurationProperties(prefix = "app")
505
+ public class AppConfig {
506
+
507
+ private String name;
508
+ private String version;
509
+ private Security security = new Security();
510
+
511
+ public static class Security {
512
+ private int tokenExpiration = 3600;
513
+ private String secretKey;
514
+
515
+ // Getters and setters
516
+ }
517
+
518
+ // Getters and setters
519
+ }
520
+
521
+ // Usage
522
+ @Service
523
+ public class MyService {
524
+
525
+ private final AppConfig appConfig;
526
+
527
+ public MyService(AppConfig appConfig) {
528
+ this.appConfig = appConfig;
529
+ }
530
+
531
+ public void printConfig() {
532
+ System.out.println("App: " + appConfig.getName());
533
+ System.out.println("Version: " + appConfig.getVersion());
534
+ }
535
+ }
536
+ ```
537
+
538
+ **Environment-Specific Configuration:**
539
+ ```properties
540
+ # application.properties (default)
541
+ spring.profiles.active=dev
542
+
543
+ # application-dev.properties
544
+ spring.datasource.url=jdbc:postgresql://localhost:5432/mydb_dev
545
+ logging.level.root=DEBUG
546
+
547
+ # application-prod.properties
548
+ spring.datasource.url=jdbc:postgresql://prod-server:5432/mydb_prod
549
+ logging.level.root=WARN
550
+ ```
551
+
552
+ **Profile-Specific Beans:**
553
+ ```java
554
+ @Configuration
555
+ public class DatabaseConfig {
556
+
557
+ @Bean
558
+ @Profile("dev")
559
+ public DataSource devDataSource() {
560
+ return new EmbeddedDatabaseBuilder()
561
+ .setType(EmbeddedDatabaseType.H2)
562
+ .build();
563
+ }
564
+
565
+ @Bean
566
+ @Profile("prod")
567
+ public DataSource prodDataSource() {
568
+ return DataSourceBuilder.create().build();
569
+ }
570
+ }
571
+ ```
572
+
573
+ ### Spring Security
574
+
575
+ Implement authentication and authorization in your application.
576
+
577
+ **Basic Security Configuration:**
578
+ ```java
579
+ @Configuration
580
+ @EnableWebSecurity
581
+ public class SecurityConfig {
582
+
583
+ @Bean
584
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
585
+ http
586
+ .csrf().disable()
587
+ .authorizeHttpRequests(auth -> auth
588
+ .requestMatchers("/api/public/**").permitAll()
589
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
590
+ .requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN")
591
+ .anyRequest().authenticated()
592
+ )
593
+ .httpBasic();
594
+
595
+ return http.build();
596
+ }
597
+
598
+ @Bean
599
+ public PasswordEncoder passwordEncoder() {
600
+ return new BCryptPasswordEncoder();
601
+ }
602
+ }
603
+ ```
604
+
605
+ **In-Memory Authentication:**
606
+ ```java
607
+ @Configuration
608
+ @EnableWebSecurity
609
+ public class SecurityConfig {
610
+
611
+ @Bean
612
+ public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
613
+ UserDetails user = User.builder()
614
+ .username("user")
615
+ .password(passwordEncoder.encode("password"))
616
+ .roles("USER")
617
+ .build();
618
+
619
+ UserDetails admin = User.builder()
620
+ .username("admin")
621
+ .password(passwordEncoder.encode("admin"))
622
+ .roles("ADMIN", "USER")
623
+ .build();
624
+
625
+ return new InMemoryUserDetailsManager(user, admin);
626
+ }
627
+ }
628
+ ```
629
+
630
+ **Database Authentication:**
631
+ ```java
632
+ @Service
633
+ public class CustomUserDetailsService implements UserDetailsService {
634
+
635
+ private final UserRepository userRepository;
636
+
637
+ public CustomUserDetailsService(UserRepository userRepository) {
638
+ this.userRepository = userRepository;
639
+ }
640
+
641
+ @Override
642
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
643
+ User user = userRepository.findByEmail(username)
644
+ .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
645
+
646
+ return org.springframework.security.core.userdetails.User.builder()
647
+ .username(user.getEmail())
648
+ .password(user.getPassword())
649
+ .roles(user.getRoles().toArray(new String[0]))
650
+ .build();
651
+ }
652
+ }
653
+
654
+ @Configuration
655
+ @EnableWebSecurity
656
+ public class SecurityConfig {
657
+
658
+ private final CustomUserDetailsService userDetailsService;
659
+
660
+ public SecurityConfig(CustomUserDetailsService userDetailsService) {
661
+ this.userDetailsService = userDetailsService;
662
+ }
663
+
664
+ @Bean
665
+ public DaoAuthenticationProvider authenticationProvider(PasswordEncoder passwordEncoder) {
666
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
667
+ provider.setUserDetailsService(userDetailsService);
668
+ provider.setPasswordEncoder(passwordEncoder);
669
+ return provider;
670
+ }
671
+ }
672
+ ```
673
+
674
+ **JWT Authentication:**
675
+ ```java
676
+ @Component
677
+ public class JwtTokenProvider {
678
+
679
+ @Value("${app.security.jwt.secret}")
680
+ private String jwtSecret;
681
+
682
+ @Value("${app.security.jwt.expiration}")
683
+ private int jwtExpiration;
684
+
685
+ public String generateToken(Authentication authentication) {
686
+ UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
687
+
688
+ Date now = new Date();
689
+ Date expiryDate = new Date(now.getTime() + jwtExpiration);
690
+
691
+ return Jwts.builder()
692
+ .setSubject(Long.toString(userPrincipal.getId()))
693
+ .setIssuedAt(now)
694
+ .setExpiration(expiryDate)
695
+ .signWith(SignatureAlgorithm.HS512, jwtSecret)
696
+ .compact();
697
+ }
698
+
699
+ public Long getUserIdFromJWT(String token) {
700
+ Claims claims = Jwts.parser()
701
+ .setSigningKey(jwtSecret)
702
+ .parseClaimsJws(token)
703
+ .getBody();
704
+
705
+ return Long.parseLong(claims.getSubject());
706
+ }
707
+
708
+ public boolean validateToken(String authToken) {
709
+ try {
710
+ Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
711
+ return true;
712
+ } catch (SignatureException | MalformedJwtException | ExpiredJwtException |
713
+ UnsupportedJwtException | IllegalArgumentException ex) {
714
+ return false;
715
+ }
716
+ }
717
+ }
718
+
719
+ @Component
720
+ public class JwtAuthenticationFilter extends OncePerRequestFilter {
721
+
722
+ private final JwtTokenProvider tokenProvider;
723
+ private final CustomUserDetailsService customUserDetailsService;
724
+
725
+ public JwtAuthenticationFilter(JwtTokenProvider tokenProvider,
726
+ CustomUserDetailsService customUserDetailsService) {
727
+ this.tokenProvider = tokenProvider;
728
+ this.customUserDetailsService = customUserDetailsService;
729
+ }
730
+
731
+ @Override
732
+ protected void doFilterInternal(HttpServletRequest request,
733
+ HttpServletResponse response,
734
+ FilterChain filterChain) throws ServletException, IOException {
735
+ try {
736
+ String jwt = getJwtFromRequest(request);
737
+
738
+ if (jwt != null && tokenProvider.validateToken(jwt)) {
739
+ Long userId = tokenProvider.getUserIdFromJWT(jwt);
740
+
741
+ UserDetails userDetails = customUserDetailsService.loadUserById(userId);
742
+ UsernamePasswordAuthenticationToken authentication =
743
+ new UsernamePasswordAuthenticationToken(
744
+ userDetails, null, userDetails.getAuthorities()
745
+ );
746
+
747
+ SecurityContextHolder.getContext().setAuthentication(authentication);
748
+ }
749
+ } catch (Exception ex) {
750
+ logger.error("Could not set user authentication", ex);
751
+ }
752
+
753
+ filterChain.doFilter(request, response);
754
+ }
755
+
756
+ private String getJwtFromRequest(HttpServletRequest request) {
757
+ String bearerToken = request.getHeader("Authorization");
758
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
759
+ return bearerToken.substring(7);
760
+ }
761
+ return null;
762
+ }
763
+ }
764
+ ```
765
+
766
+ ## API Reference
767
+
768
+ ### Common Annotations
769
+
770
+ **Core Spring Annotations:**
771
+ - `@SpringBootApplication`: Main application class
772
+ - `@Component`: Generic component
773
+ - `@Service`: Service layer component
774
+ - `@Repository`: Data access layer component
775
+ - `@Configuration`: Configuration class
776
+ - `@Bean`: Bean definition method
777
+ - `@Autowired`: Dependency injection
778
+ - `@Value`: Inject property values
779
+ - `@Profile`: Conditional beans based on profiles
780
+
781
+ **Web Annotations:**
782
+ - `@RestController`: REST API controller
783
+ - `@Controller`: MVC controller
784
+ - `@RequestMapping`: Map HTTP requests
785
+ - `@GetMapping`: Map GET requests
786
+ - `@PostMapping`: Map POST requests
787
+ - `@PutMapping`: Map PUT requests
788
+ - `@DeleteMapping`: Map DELETE requests
789
+ - `@PatchMapping`: Map PATCH requests
790
+ - `@PathVariable`: Extract path variables
791
+ - `@RequestParam`: Extract query parameters
792
+ - `@RequestBody`: Extract request body
793
+ - `@RequestHeader`: Extract request headers
794
+ - `@ResponseStatus`: Set response status
795
+
796
+ **Data Annotations:**
797
+ - `@Entity`: JPA entity
798
+ - `@Table`: Table mapping
799
+ - `@Id`: Primary key
800
+ - `@GeneratedValue`: Auto-generated values
801
+ - `@Column`: Column mapping
802
+ - `@OneToOne`: One-to-one relationship
803
+ - `@OneToMany`: One-to-many relationship
804
+ - `@ManyToOne`: Many-to-one relationship
805
+ - `@ManyToMany`: Many-to-many relationship
806
+ - `@JoinColumn`: Join column
807
+ - `@JoinTable`: Join table
808
+
809
+ **Validation Annotations:**
810
+ - `@Valid`: Enable validation
811
+ - `@NotNull`: Field cannot be null
812
+ - `@NotEmpty`: Field cannot be empty
813
+ - `@NotBlank`: Field cannot be blank
814
+ - `@Size`: String or collection size
815
+ - `@Min`: Minimum value
816
+ - `@Max`: Maximum value
817
+ - `@Email`: Email format
818
+ - `@Pattern`: Regex pattern
819
+
820
+ **Transaction Annotations:**
821
+ - `@Transactional`: Enable transaction management
822
+ - `@Transactional(readOnly = true)`: Read-only transaction
823
+
824
+ **Security Annotations:**
825
+ - `@EnableWebSecurity`: Enable security
826
+ - `@PreAuthorize`: Method-level authorization
827
+ - `@PostAuthorize`: Post-method authorization
828
+ - `@Secured`: Role-based access
829
+
830
+ **Async and Scheduling:**
831
+ - `@EnableAsync`: Enable async processing
832
+ - `@Async`: Async method
833
+ - `@EnableScheduling`: Enable scheduling
834
+ - `@Scheduled`: Scheduled method
835
+
836
+ ## Workflow Patterns
837
+
838
+ ### REST API Design Pattern
839
+
840
+ **Complete CRUD REST API:**
841
+ ```java
842
+ // Entity
843
+ @Entity
844
+ @Table(name = "products")
845
+ public class Product {
846
+ @Id
847
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
848
+ private Long id;
849
+
850
+ @NotBlank(message = "Name is required")
851
+ private String name;
852
+
853
+ @NotBlank(message = "Description is required")
854
+ private String description;
855
+
856
+ @NotNull(message = "Price is required")
857
+ @Min(value = 0, message = "Price must be positive")
858
+ private BigDecimal price;
859
+
860
+ @NotNull(message = "Stock is required")
861
+ @Min(value = 0, message = "Stock must be positive")
862
+ private Integer stock;
863
+
864
+ private LocalDateTime createdAt;
865
+ private LocalDateTime updatedAt;
866
+
867
+ @PrePersist
868
+ protected void onCreate() {
869
+ createdAt = LocalDateTime.now();
870
+ updatedAt = LocalDateTime.now();
871
+ }
872
+
873
+ @PreUpdate
874
+ protected void onUpdate() {
875
+ updatedAt = LocalDateTime.now();
876
+ }
877
+
878
+ // Getters and setters
879
+ }
880
+
881
+ // Repository
882
+ @Repository
883
+ public interface ProductRepository extends JpaRepository<Product, Long> {
884
+ List<Product> findByNameContaining(String name);
885
+ List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);
886
+ }
887
+
888
+ // Service
889
+ @Service
890
+ @Transactional
891
+ public class ProductService {
892
+
893
+ private final ProductRepository productRepository;
894
+
895
+ public ProductService(ProductRepository productRepository) {
896
+ this.productRepository = productRepository;
897
+ }
898
+
899
+ @Transactional(readOnly = true)
900
+ public Page<Product> findAll(Pageable pageable) {
901
+ return productRepository.findAll(pageable);
902
+ }
903
+
904
+ @Transactional(readOnly = true)
905
+ public Optional<Product> findById(Long id) {
906
+ return productRepository.findById(id);
907
+ }
908
+
909
+ public Product create(Product product) {
910
+ return productRepository.save(product);
911
+ }
912
+
913
+ public Optional<Product> update(Long id, Product productDetails) {
914
+ return productRepository.findById(id)
915
+ .map(product -> {
916
+ product.setName(productDetails.getName());
917
+ product.setDescription(productDetails.getDescription());
918
+ product.setPrice(productDetails.getPrice());
919
+ product.setStock(productDetails.getStock());
920
+ return productRepository.save(product);
921
+ });
922
+ }
923
+
924
+ public boolean delete(Long id) {
925
+ return productRepository.findById(id)
926
+ .map(product -> {
927
+ productRepository.delete(product);
928
+ return true;
929
+ })
930
+ .orElse(false);
931
+ }
932
+ }
933
+
934
+ // Controller
935
+ @RestController
936
+ @RequestMapping("/api/products")
937
+ public class ProductController {
938
+
939
+ private final ProductService productService;
940
+
941
+ public ProductController(ProductService productService) {
942
+ this.productService = productService;
943
+ }
944
+
945
+ @GetMapping
946
+ public Page<Product> getAllProducts(
947
+ @RequestParam(defaultValue = "0") int page,
948
+ @RequestParam(defaultValue = "10") int size,
949
+ @RequestParam(defaultValue = "id") String sortBy) {
950
+ Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
951
+ return productService.findAll(pageable);
952
+ }
953
+
954
+ @GetMapping("/{id}")
955
+ public ResponseEntity<Product> getProductById(@PathVariable Long id) {
956
+ return productService.findById(id)
957
+ .map(ResponseEntity::ok)
958
+ .orElse(ResponseEntity.notFound().build());
959
+ }
960
+
961
+ @PostMapping
962
+ public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
963
+ Product created = productService.create(product);
964
+ URI location = ServletUriComponentsBuilder
965
+ .fromCurrentRequest()
966
+ .path("/{id}")
967
+ .buildAndExpand(created.getId())
968
+ .toUri();
969
+ return ResponseEntity.created(location).body(created);
970
+ }
971
+
972
+ @PutMapping("/{id}")
973
+ public ResponseEntity<Product> updateProduct(
974
+ @PathVariable Long id,
975
+ @Valid @RequestBody Product product) {
976
+ return productService.update(id, product)
977
+ .map(ResponseEntity::ok)
978
+ .orElse(ResponseEntity.notFound().build());
979
+ }
980
+
981
+ @DeleteMapping("/{id}")
982
+ public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
983
+ if (productService.delete(id)) {
984
+ return ResponseEntity.noContent().build();
985
+ }
986
+ return ResponseEntity.notFound().build();
987
+ }
988
+ }
989
+ ```
990
+
991
+ ### Exception Handling Pattern
992
+
993
+ **Global Exception Handler:**
994
+ ```java
995
+ // Custom exceptions
996
+ public class ResourceNotFoundException extends RuntimeException {
997
+ public ResourceNotFoundException(String message) {
998
+ super(message);
999
+ }
1000
+ }
1001
+
1002
+ public class BadRequestException extends RuntimeException {
1003
+ public BadRequestException(String message) {
1004
+ super(message);
1005
+ }
1006
+ }
1007
+
1008
+ // Error response
1009
+ public class ErrorResponse {
1010
+ private LocalDateTime timestamp;
1011
+ private int status;
1012
+ private String error;
1013
+ private String message;
1014
+ private String path;
1015
+
1016
+ // Constructors, getters, setters
1017
+ }
1018
+
1019
+ // Global exception handler
1020
+ @RestControllerAdvice
1021
+ public class GlobalExceptionHandler {
1022
+
1023
+ @ExceptionHandler(ResourceNotFoundException.class)
1024
+ public ResponseEntity<ErrorResponse> handleResourceNotFound(
1025
+ ResourceNotFoundException ex,
1026
+ WebRequest request) {
1027
+ ErrorResponse error = new ErrorResponse(
1028
+ LocalDateTime.now(),
1029
+ HttpStatus.NOT_FOUND.value(),
1030
+ "Not Found",
1031
+ ex.getMessage(),
1032
+ request.getDescription(false).replace("uri=", "")
1033
+ );
1034
+ return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
1035
+ }
1036
+
1037
+ @ExceptionHandler(BadRequestException.class)
1038
+ public ResponseEntity<ErrorResponse> handleBadRequest(
1039
+ BadRequestException ex,
1040
+ WebRequest request) {
1041
+ ErrorResponse error = new ErrorResponse(
1042
+ LocalDateTime.now(),
1043
+ HttpStatus.BAD_REQUEST.value(),
1044
+ "Bad Request",
1045
+ ex.getMessage(),
1046
+ request.getDescription(false).replace("uri=", "")
1047
+ );
1048
+ return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
1049
+ }
1050
+
1051
+ @ExceptionHandler(MethodArgumentNotValidException.class)
1052
+ public ResponseEntity<Map<String, Object>> handleValidationErrors(
1053
+ MethodArgumentNotValidException ex) {
1054
+ Map<String, Object> errors = new HashMap<>();
1055
+ errors.put("timestamp", LocalDateTime.now());
1056
+ errors.put("status", HttpStatus.BAD_REQUEST.value());
1057
+
1058
+ Map<String, String> fieldErrors = new HashMap<>();
1059
+ ex.getBindingResult().getFieldErrors().forEach(error ->
1060
+ fieldErrors.put(error.getField(), error.getDefaultMessage())
1061
+ );
1062
+ errors.put("errors", fieldErrors);
1063
+
1064
+ return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
1065
+ }
1066
+
1067
+ @ExceptionHandler(Exception.class)
1068
+ public ResponseEntity<ErrorResponse> handleGlobalException(
1069
+ Exception ex,
1070
+ WebRequest request) {
1071
+ ErrorResponse error = new ErrorResponse(
1072
+ LocalDateTime.now(),
1073
+ HttpStatus.INTERNAL_SERVER_ERROR.value(),
1074
+ "Internal Server Error",
1075
+ ex.getMessage(),
1076
+ request.getDescription(false).replace("uri=", "")
1077
+ );
1078
+ return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
1079
+ }
1080
+ }
1081
+ ```
1082
+
1083
+ ### Database Integration Pattern
1084
+
1085
+ **Complete Database Setup:**
1086
+ ```java
1087
+ // application.yml
1088
+ /*
1089
+ spring:
1090
+ datasource:
1091
+ url: jdbc:postgresql://localhost:5432/mydb
1092
+ username: user
1093
+ password: password
1094
+ jpa:
1095
+ hibernate:
1096
+ ddl-auto: validate
1097
+ show-sql: true
1098
+ properties:
1099
+ hibernate:
1100
+ dialect: org.hibernate.dialect.PostgreSQLDialect
1101
+ */
1102
+
1103
+ // Flyway migrations (db/migration/V1__Create_users_table.sql)
1104
+ /*
1105
+ CREATE TABLE users (
1106
+ id BIGSERIAL PRIMARY KEY,
1107
+ email VARCHAR(255) NOT NULL UNIQUE,
1108
+ name VARCHAR(255) NOT NULL,
1109
+ password VARCHAR(255) NOT NULL,
1110
+ created_at TIMESTAMP NOT NULL,
1111
+ updated_at TIMESTAMP NOT NULL
1112
+ );
1113
+
1114
+ CREATE INDEX idx_users_email ON users(email);
1115
+ */
1116
+
1117
+ // Entity with auditing
1118
+ @Entity
1119
+ @Table(name = "users")
1120
+ @EntityListeners(AuditingEntityListener.class)
1121
+ public class User {
1122
+ @Id
1123
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
1124
+ private Long id;
1125
+
1126
+ @Column(nullable = false, unique = true)
1127
+ private String email;
1128
+
1129
+ @Column(nullable = false)
1130
+ private String name;
1131
+
1132
+ @Column(nullable = false)
1133
+ private String password;
1134
+
1135
+ @CreatedDate
1136
+ @Column(name = "created_at", nullable = false, updatable = false)
1137
+ private LocalDateTime createdAt;
1138
+
1139
+ @LastModifiedDate
1140
+ @Column(name = "updated_at")
1141
+ private LocalDateTime updatedAt;
1142
+
1143
+ // Getters and setters
1144
+ }
1145
+
1146
+ // Enable JPA auditing
1147
+ @Configuration
1148
+ @EnableJpaAuditing
1149
+ public class JpaConfig {
1150
+ }
1151
+ ```
1152
+
1153
+ ### Testing Pattern
1154
+
1155
+ **Unit Tests:**
1156
+ ```java
1157
+ @SpringBootTest
1158
+ class UserServiceTest {
1159
+
1160
+ @Mock
1161
+ private UserRepository userRepository;
1162
+
1163
+ @InjectMocks
1164
+ private UserService userService;
1165
+
1166
+ @BeforeEach
1167
+ void setUp() {
1168
+ MockitoAnnotations.openMocks(this);
1169
+ }
1170
+
1171
+ @Test
1172
+ void testFindById_Success() {
1173
+ User user = new User();
1174
+ user.setId(1L);
1175
+ user.setEmail("test@example.com");
1176
+
1177
+ when(userRepository.findById(1L)).thenReturn(Optional.of(user));
1178
+
1179
+ Optional<User> result = userService.findById(1L);
1180
+
1181
+ assertTrue(result.isPresent());
1182
+ assertEquals("test@example.com", result.get().getEmail());
1183
+ verify(userRepository, times(1)).findById(1L);
1184
+ }
1185
+
1186
+ @Test
1187
+ void testFindById_NotFound() {
1188
+ when(userRepository.findById(1L)).thenReturn(Optional.empty());
1189
+
1190
+ Optional<User> result = userService.findById(1L);
1191
+
1192
+ assertFalse(result.isPresent());
1193
+ }
1194
+ }
1195
+ ```
1196
+
1197
+ **Integration Tests:**
1198
+ ```java
1199
+ @SpringBootTest
1200
+ @AutoConfigureMockMvc
1201
+ @Transactional
1202
+ class UserControllerIntegrationTest {
1203
+
1204
+ @Autowired
1205
+ private MockMvc mockMvc;
1206
+
1207
+ @Autowired
1208
+ private ObjectMapper objectMapper;
1209
+
1210
+ @Autowired
1211
+ private UserRepository userRepository;
1212
+
1213
+ @Test
1214
+ void testCreateUser_Success() throws Exception {
1215
+ User user = new User();
1216
+ user.setEmail("test@example.com");
1217
+ user.setName("Test User");
1218
+
1219
+ mockMvc.perform(post("/api/users")
1220
+ .contentType(MediaType.APPLICATION_JSON)
1221
+ .content(objectMapper.writeValueAsString(user)))
1222
+ .andExpect(status().isCreated())
1223
+ .andExpect(jsonPath("$.email").value("test@example.com"))
1224
+ .andExpect(jsonPath("$.name").value("Test User"));
1225
+ }
1226
+
1227
+ @Test
1228
+ void testGetUser_Success() throws Exception {
1229
+ User user = new User();
1230
+ user.setEmail("test@example.com");
1231
+ user.setName("Test User");
1232
+ User saved = userRepository.save(user);
1233
+
1234
+ mockMvc.perform(get("/api/users/" + saved.getId()))
1235
+ .andExpect(status().isOk())
1236
+ .andExpect(jsonPath("$.id").value(saved.getId()))
1237
+ .andExpect(jsonPath("$.email").value("test@example.com"));
1238
+ }
1239
+
1240
+ @Test
1241
+ void testGetUser_NotFound() throws Exception {
1242
+ mockMvc.perform(get("/api/users/999"))
1243
+ .andExpect(status().isNotFound());
1244
+ }
1245
+ }
1246
+ ```
1247
+
1248
+ ## Best Practices
1249
+
1250
+ ### 1. Use Constructor Injection
1251
+
1252
+ Constructor injection is the recommended approach for dependency injection.
1253
+
1254
+ ```java
1255
+ // Good - Constructor injection
1256
+ @Service
1257
+ public class UserService {
1258
+ private final UserRepository userRepository;
1259
+ private final EmailService emailService;
1260
+
1261
+ public UserService(UserRepository userRepository, EmailService emailService) {
1262
+ this.userRepository = userRepository;
1263
+ this.emailService = emailService;
1264
+ }
1265
+ }
1266
+
1267
+ // Bad - Field injection
1268
+ @Service
1269
+ public class UserService {
1270
+ @Autowired
1271
+ private UserRepository userRepository;
1272
+ }
1273
+ ```
1274
+
1275
+ ### 2. Use DTOs for API Requests/Responses
1276
+
1277
+ Don't expose entities directly through REST APIs.
1278
+
1279
+ ```java
1280
+ // DTO
1281
+ public class UserDTO {
1282
+ private Long id;
1283
+ private String email;
1284
+ private String name;
1285
+
1286
+ // No password field exposed
1287
+ // Getters and setters
1288
+ }
1289
+
1290
+ // Mapper
1291
+ @Component
1292
+ public class UserMapper {
1293
+ public UserDTO toDTO(User user) {
1294
+ UserDTO dto = new UserDTO();
1295
+ dto.setId(user.getId());
1296
+ dto.setEmail(user.getEmail());
1297
+ dto.setName(user.getName());
1298
+ return dto;
1299
+ }
1300
+
1301
+ public User toEntity(UserDTO dto) {
1302
+ User user = new User();
1303
+ user.setEmail(dto.getEmail());
1304
+ user.setName(dto.getName());
1305
+ return user;
1306
+ }
1307
+ }
1308
+
1309
+ // Controller
1310
+ @RestController
1311
+ @RequestMapping("/api/users")
1312
+ public class UserController {
1313
+ private final UserService userService;
1314
+ private final UserMapper userMapper;
1315
+
1316
+ @GetMapping("/{id}")
1317
+ public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
1318
+ return userService.findById(id)
1319
+ .map(userMapper::toDTO)
1320
+ .map(ResponseEntity::ok)
1321
+ .orElse(ResponseEntity.notFound().build());
1322
+ }
1323
+ }
1324
+ ```
1325
+
1326
+ ### 3. Use Validation
1327
+
1328
+ Always validate input data.
1329
+
1330
+ ```java
1331
+ // Entity with validation
1332
+ @Entity
1333
+ public class User {
1334
+ @NotBlank(message = "Email is required")
1335
+ @Email(message = "Email should be valid")
1336
+ private String email;
1337
+
1338
+ @NotBlank(message = "Name is required")
1339
+ @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
1340
+ private String name;
1341
+
1342
+ @NotBlank(message = "Password is required")
1343
+ @Size(min = 8, message = "Password must be at least 8 characters")
1344
+ private String password;
1345
+ }
1346
+
1347
+ // Controller
1348
+ @PostMapping
1349
+ public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
1350
+ // Validation happens automatically
1351
+ return ResponseEntity.ok(userService.save(user));
1352
+ }
1353
+ ```
1354
+
1355
+ ### 4. Use Transactions Properly
1356
+
1357
+ Mark service methods with appropriate transaction settings.
1358
+
1359
+ ```java
1360
+ @Service
1361
+ @Transactional
1362
+ public class OrderService {
1363
+
1364
+ @Transactional(readOnly = true)
1365
+ public List<Order> findAll() {
1366
+ return orderRepository.findAll();
1367
+ }
1368
+
1369
+ @Transactional
1370
+ public Order createOrder(Order order) {
1371
+ // Multiple database operations in one transaction
1372
+ Order saved = orderRepository.save(order);
1373
+ inventoryService.decreaseStock(order.getItems());
1374
+ emailService.sendOrderConfirmation(saved);
1375
+ return saved;
1376
+ }
1377
+ }
1378
+ ```
1379
+
1380
+ ### 5. Use Pagination
1381
+
1382
+ Always paginate large datasets.
1383
+
1384
+ ```java
1385
+ @GetMapping
1386
+ public Page<Product> getProducts(
1387
+ @RequestParam(defaultValue = "0") int page,
1388
+ @RequestParam(defaultValue = "20") int size,
1389
+ @RequestParam(defaultValue = "id") String sortBy) {
1390
+ Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
1391
+ return productService.findAll(pageable);
1392
+ }
1393
+ ```
1394
+
1395
+ ### 6. Handle Exceptions Globally
1396
+
1397
+ Use `@RestControllerAdvice` for centralized exception handling.
1398
+
1399
+ ```java
1400
+ @RestControllerAdvice
1401
+ public class GlobalExceptionHandler {
1402
+
1403
+ @ExceptionHandler(ResourceNotFoundException.class)
1404
+ public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
1405
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
1406
+ .body(new ErrorResponse(ex.getMessage()));
1407
+ }
1408
+ }
1409
+ ```
1410
+
1411
+ ### 7. Use Logging
1412
+
1413
+ Implement proper logging throughout your application.
1414
+
1415
+ ```java
1416
+ @Service
1417
+ public class UserService {
1418
+ private static final Logger logger = LoggerFactory.getLogger(UserService.class);
1419
+
1420
+ public User createUser(User user) {
1421
+ logger.info("Creating user with email: {}", user.getEmail());
1422
+ try {
1423
+ User saved = userRepository.save(user);
1424
+ logger.info("User created successfully with id: {}", saved.getId());
1425
+ return saved;
1426
+ } catch (Exception e) {
1427
+ logger.error("Error creating user: {}", e.getMessage(), e);
1428
+ throw e;
1429
+ }
1430
+ }
1431
+ }
1432
+ ```
1433
+
1434
+ ### 8. Secure Your Endpoints
1435
+
1436
+ Implement proper authentication and authorization.
1437
+
1438
+ ```java
1439
+ @Configuration
1440
+ @EnableWebSecurity
1441
+ public class SecurityConfig {
1442
+
1443
+ @Bean
1444
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
1445
+ http
1446
+ .authorizeHttpRequests(auth -> auth
1447
+ .requestMatchers("/api/public/**").permitAll()
1448
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
1449
+ .anyRequest().authenticated()
1450
+ )
1451
+ .oauth2ResourceServer().jwt();
1452
+
1453
+ return http.build();
1454
+ }
1455
+ }
1456
+ ```
1457
+
1458
+ ### 9. Use Database Migrations
1459
+
1460
+ Use Flyway or Liquibase for database version control.
1461
+
1462
+ ```sql
1463
+ -- V1__Create_users_table.sql
1464
+ CREATE TABLE users (
1465
+ id BIGSERIAL PRIMARY KEY,
1466
+ email VARCHAR(255) NOT NULL UNIQUE,
1467
+ name VARCHAR(255) NOT NULL,
1468
+ created_at TIMESTAMP NOT NULL
1469
+ );
1470
+
1471
+ -- V2__Add_password_column.sql
1472
+ ALTER TABLE users ADD COLUMN password VARCHAR(255);
1473
+ ```
1474
+
1475
+ ### 10. Monitor Your Application
1476
+
1477
+ Use Spring Boot Actuator for monitoring.
1478
+
1479
+ ```properties
1480
+ # application.properties
1481
+ management.endpoints.web.exposure.include=health,info,metrics
1482
+ management.endpoint.health.show-details=always
1483
+ ```
1484
+
1485
+ ## Examples
1486
+
1487
+ See EXAMPLES.md for detailed code examples including:
1488
+ - Basic Spring Boot Application
1489
+ - REST API with CRUD Operations
1490
+ - Database Integration with JPA
1491
+ - Custom Queries and Specifications
1492
+ - Request Validation
1493
+ - Exception Handling
1494
+ - Authentication with JWT
1495
+ - Role-Based Authorization
1496
+ - File Upload/Download
1497
+ - Caching with Redis
1498
+ - Async Processing
1499
+ - Scheduled Tasks
1500
+ - Multiple Database Configuration
1501
+ - Actuator and Monitoring
1502
+ - Docker Deployment
1503
+
1504
+ ## Summary
1505
+
1506
+ This Spring Boot development skill covers:
1507
+
1508
+ 1. **Auto-Configuration**: Automatic configuration based on dependencies
1509
+ 2. **Dependency Injection**: IoC container, constructor injection, component stereotypes
1510
+ 3. **REST APIs**: Controllers, request mapping, response handling
1511
+ 4. **Spring Data JPA**: Entities, repositories, relationships, queries
1512
+ 5. **Configuration**: Properties, YAML, profiles, custom properties
1513
+ 6. **Security**: Authentication, authorization, JWT, role-based access
1514
+ 7. **Exception Handling**: Global exception handling, custom exceptions
1515
+ 8. **Testing**: Unit tests, integration tests, MockMvc
1516
+ 9. **Best Practices**: DTOs, validation, transactions, pagination, logging
1517
+ 10. **Production Ready**: Actuator, monitoring, database migrations, deployment
1518
+
1519
+ The patterns and examples are based on official Spring Boot documentation (Trust Score: 7.5) and represent modern enterprise Java development practices.