.cursor/skills/spring-boot-patterns/SKILL.md
Spring Boot coding standards, patterns, and conventions for this project. Uses SLF4J API with Log4j2 as the logging implementation. Loaded dynamically when implementing Java/Spring Boot code.
npx skillsauth add BhumitThakkar/cursor-kit spring-boot-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
com.example.project
├── controller/ # @RestController — HTTP boundary only, no business logic
├── service/ # @Service — all business logic lives here
├── repository/ # @Repository — JPA interfaces only
├── dto/ # Request/Response DTOs with validation annotations
├── entity/ # @Entity — JPA-managed database objects
├── config/ # @Configuration — beans, security, CORS
└── exception/ # @ControllerAdvice + custom exceptions
@RestController
@RequestMapping("/api/v1/resource")
@RequiredArgsConstructor
public class ResourceController {
private static final Logger log = LoggerFactory.getLogger(ResourceController.class);
private final ResourceService resourceService;
/**
* Creates a new resource.
*
* @param request the creation request
* @param result binding result for validation errors
* @return created resource response
*/
@PostMapping
public ResponseEntity<ResourceResponse> create(
@Valid @RequestBody ResourceRequest request,
BindingResult result) {
if (result.hasErrors()) {
throw new ValidationException(result);
}
log.info("Creating resource: {}", request.getName());
return ResponseEntity.status(HttpStatus.CREATED)
.body(resourceService.create(request));
}
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ResourceService {
private static final Logger log = LoggerFactory.getLogger(ResourceService.class);
private final ResourceRepository resourceRepository;
/**
* Creates a new resource after validating business rules.
*/
@Transactional
public ResourceResponse create(ResourceRequest request) {
log.debug("Validating resource creation for: {}", request.getName());
// business logic here
Resource saved = resourceRepository.save(toEntity(request));
log.info("Resource created with id: {}", saved.getId());
return toResponse(saved);
}
}
public class ResourceRequest {
@NotBlank(message = "Name is required")
@Size(max = 100, message = "Name must not exceed 100 characters")
private String name;
@NotNull(message = "Type is required")
private ResourceType type;
@Email(message = "Must be a valid email")
private String contactEmail;
}
// CORRECT
Path filePath = Paths.get(baseDir, fileName);
Path resolved = rootPath.resolve(relativePath).normalize();
// WRONG — never do this
String path = baseDir + "/" + fileName;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handleValidation(ValidationException ex) {
log.warn("Validation failed: {}", ex.getMessage());
return ResponseEntity.badRequest().body(new ErrorResponse(ex.getErrors()));
}
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException ex) {
log.warn("Resource not found: {}", ex.getMessage());
return ResponseEntity.notFound().build();
}
}
// application.properties — all environment-specific values live here
app.upload.dir=${UPLOAD_DIR:/tmp/uploads}
app.max-file-size=${MAX_FILE_SIZE:5242880}
// Injected via @Value
@Value("${app.upload.dir}")
private String uploadDir;
log.error — exceptions that need immediate attentionlog.warn — recoverable issues, validation failureslog.info — business events (resource created, payment processed)log.debug — internal state useful during development onlyThis project standardizes on SLF4J as the logging API and Apache Log4j2 as the implementation (not Logback).
<!-- pom.xml — do not use spring-boot-starter-logging alongside this -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
spring-boot-starter-logging, exclude it from that artifact so only one SLF4J binding exists at runtime.log4j2-spring.xml (or log4j2.xml) on the classpath so Spring’s LoggingSystem and profile support work as expected.logging.level.com.example=DEBUG in application.properties / YAML.log4j2*.xml.private static final Logger log = LoggerFactory.getLogger(MyClass.class); with imports from org.slf4j only.log.info("Created booking {}", bookingId); — never string concatenation for variables.log.error("Payment failed", exception);%X{correlationId} (or equivalent) in the pattern layout so logs are greppable in ELK/Loki/etc.org.apache.logging.log4j.LogManager.getLogger in normal application classes — stick to SLF4J so the implementation can change with one dependency swap.log4j:log4j) or bridges that pull it in.%m{...} style features in untrusted contexts).log4j2.version (when managed explicitly) to a current supported release; run dependency-check in CI.@Mapper(componentModel = "spring"); never hand-copy dozens of fields in services.OncePerRequestFilter; clear in finally to avoid thread-pool leaks.health, readiness, liveness as needed; secure endpoints in prod (management.endpoints.web.exposure).@EntityGraph, JOIN FETCH, or DTO projections for hot reads; verify with integration tests and logging of query counts in dev.Optional in service returns where absence is normal; validate null at controller edges./api/v1/...; breaking changes require /v2 or header versioning per ADR — never silent breakage.LogManager in app code@RestController methods@Valid + BindingResult on request bodies where applicabledevelopment
Read-only checklist for Jakarta Bean Validation on Spring API models (DTOs, request bodies). Produces a markdown report with PASS/FAIL; does not modify source files.
data-ai
Thymeleaf + Spring Boot UI conventions, fragment patterns, form handling, and accessibility standards for this project.
development
QA-oriented playbook for verifying test coverage on new or changed code (JaCoCo / Maven Gradle) against project quality gate expectations. Read-only analysis instructions unless user authorizes runs.
testing
Maintain AGENTS.md roster and disabled.txt without deleting agent files without explicit approval.