spring-boot

Spring Boot 3 Enterprise Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "spring-boot" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-spring-boot

Spring Boot 3 Enterprise Patterns

Full Reference: See production.md for configuration profiles, health checks, logging, graceful shutdown, and caching.

Controller with DTOs

@RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor @Tag(name = "Users", description = "User management endpoints") public class UserController {

private final UserService userService;

@GetMapping
public ResponseEntity<List<UserResponse>> findAll(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
    return ResponseEntity.ok(userService.findAll(page, size));
}

@GetMapping("/{id}")
public ResponseEntity<UserResponse> findById(@PathVariable Long id) {
    return ResponseEntity.ok(userService.findById(id));
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<UserResponse> create(@Valid @RequestBody CreateUserRequest dto) {
    return ResponseEntity.status(HttpStatus.CREATED).body(userService.create(dto));
}

@PutMapping("/{id}")
public ResponseEntity<UserResponse> update(
        @PathVariable Long id, @Valid @RequestBody UpdateUserRequest dto) {
    return ResponseEntity.ok(userService.update(id, dto));
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public ResponseEntity<Void> delete(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();
}

}

Service with MapStruct

@Service @Transactional(readOnly = true) @RequiredArgsConstructor public class UserServiceImpl implements UserService {

private final UserRepository userRepository;
private final UserMapper userMapper;
private final PasswordEncoder passwordEncoder;

@Override
public List<UserResponse> findAll(int page, int size) {
    return userRepository.findAll(PageRequest.of(page, size))
        .map(userMapper::toResponse).getContent();
}

@Override
public UserResponse findById(Long id) {
    return userRepository.findById(id)
        .map(userMapper::toResponse)
        .orElseThrow(() -> new ResourceNotFoundException("User", "id", id));
}

@Override
@Transactional
public UserResponse create(CreateUserRequest dto) {
    if (userRepository.existsByEmail(dto.getEmail())) {
        throw new BadRequestException("Email already registered");
    }
    User user = userMapper.toEntity(dto);
    user.setPassword(passwordEncoder.encode(dto.getPassword()));
    return userMapper.toResponse(userRepository.save(user));
}

}

MapStruct Mapper

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface UserMapper {

UserResponse toResponse(User user);

@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "password", ignore = true)
User toEntity(CreateUserRequest dto);

@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateEntity(UpdateUserRequest dto, @MappingTarget User user);

}

Entity with Lombok & Auditing

@Entity @Table(name = "users") @Data @NoArgsConstructor @AllArgsConstructor @Builder @EntityListeners(AuditingEntityListener.class) public class User {

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

@Column(nullable = false, length = 100)
private String name;

@Column(unique = true, nullable = false)
private String email;

@Column(nullable = false)
private String password;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserRole role = UserRole.USER;

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;

}

DTOs with Validation

@Data public class CreateUserRequest { @NotBlank(message = "Name is required") @Size(min = 2, max = 100) private String name;

@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;

@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;

}

@Data @Builder public class UserResponse { private Long id; private String name; private String email; private UserRole role; private LocalDateTime createdAt; }

Global Exception Handler

@RestControllerAdvice @Slf4j public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(ex.getMessage()));
}

@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> handleBadRequest(BadRequestException ex) {
    return ResponseEntity.badRequest().body(ErrorResponse.of(ex.getMessage()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
    Map<String, String> errors = ex.getBindingResult().getFieldErrors().stream()
        .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (a, b) -> a));
    return ResponseEntity.badRequest().body(ErrorResponse.of("Validation failed", errors));
}

}

JWT Security Configuration

@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig {

private final JwtAuthenticationFilter jwtAuthFilter;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
        .csrf(AbstractHttpConfigurer::disable)
        .cors(Customizer.withDefaults())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/v1/auth/**").permitAll()
            .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
            .requestMatchers("/actuator/health").permitAll()
            .anyRequest().authenticated())
        .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
        .build();
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

}

Flyway Migration

-- V1__create_users_table.sql CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT 'USER', status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP );

CREATE INDEX idx_users_email ON users(email);

Key Annotations

Annotation Purpose

@RestController

REST controller

@RequiredArgsConstructor

Lombok constructor injection

@Transactional

Transaction management

@Valid

Bean validation

@Mapper

MapStruct mapper

@EntityListeners

JPA auditing

Anti-Patterns

Anti-Pattern Why It's Bad Correct Approach

Manual constructor injection Verbose, error-prone Use @RequiredArgsConstructor

Manual DTO mapping Boilerplate code Use MapStruct

Try-catch in every controller Code duplication Use @ControllerAdvice

Forget @Transactional

Data inconsistency Always use for write operations

Manual schema changes Migration chaos Use Flyway or Liquibase

Quick Troubleshooting

Problem Likely Cause Solution

LazyInitializationException Open-in-view disabled Fetch data in transaction

401 Unauthorized Security misconfigured Check SecurityFilterChain

Validation not working Missing @Valid

Add @Valid on @RequestBody

Mapper not found MapStruct not processed Run mvn compile

Flyway migration fails Checksum mismatch Fix migration or use repair

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-boot for comprehensive documentation.

Note: For JPA and Security, use dedicated skills spring-data-jpa and spring-security .

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review