/java-patterns
Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class.
$ npx -y skills add softspark/ai-toolkit --skill java-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/java-patterns
Context preview
The summary Claude sees to decide when to auto-load this skill.
Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class.
SKILL.md
java-patterns.SKILL.mdname: java-patterns
description: "Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class."
effort: medium
user-invocable: false
allowed-tools: Read
Java Patterns Skill
Project Structure
Maven / Gradle Standard Layout
my-app/
├── pom.xml (or build.gradle.kts + settings.gradle.kts)
├── src/
│ ├── main/
│ │ ├── java/com/example/myapp/
│ │ │ ├── MyApplication.java
│ │ │ ├── config/
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ ├── model/
│ │ │ │ ├── entity/
│ │ │ │ └── dto/
│ │ │ └── exception/
│ │ └── resources/
│ │ ├── application.yml
│ │ └── db/migration/
│ └── test/
│ ├── java/com/example/myapp/
│ └── resources/application-test.yml
└── target/ (or build/)
Multi-Module
parent/
├── pom.xml (packaging=pom)
├── common/ (shared utilities)
├── domain/ (entities, business rules)
├── api/ (REST controllers, DTOs)
└── app/ (Spring Boot main, wiring)
---
Idioms / Code Style
Records (Java 16+)
public record UserDto(Long id, String name, String email) {
public UserDto { // compact constructor for validation
Objects.requireNonNull(name, "name must not be null");
Objects.requireNonNull(email, "email must not be null");
}
}Sealed Classes (Java 17+)
public sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}
public record Circle(double radius) implements Shape {
public double area() { return Math.PI * radius * radius; }
}
public record Rectangle(double w, double h) implements Shape {
public double area() { return w * h; }
}
public record Triangle(double base, double height) implements Shape {
public double area() { return 0.5 * base * height; }
}Switch Expressions (Java 14+)
String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle r=" + c.radius();
case Rectangle r -> "Rect %sx%s".formatted(r.w(), r.h());
case Triangle t -> "Triangle base=" + t.base();
};
}
// Guard patterns (Java 21+)
String classify(Shape shape) {
return switch (shape) {
case Circle c when c.radius() > 100 -> "large circle";
case Circle c -> "small circle";
case Rectangle r -> "rectangle";
case Triangle t -> "triangle";
};
}var, Streams, Optional
// var -- use when RHS makes type obvious
var users = new ArrayList<User>();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Avoid: var result = service.process(data); -- type unclear
// Streams
List<String> names = users.stream()
.filter(User::isActive)
.map(User::name)
.sorted()
.toList(); // Java 16+, unmodifiable
Map<Department, List<User>> byDept = users.stream()
.collect(Collectors.groupingBy(User::department));
// Optional -- return type only, never as field or parameter
String city = findByEmail(email)
.map(User::address)
.map(Address::city)
.orElse("Unknown");
// Never call .get() without guard -- use orElse/orElseThrow
// Text blocks (Java 15+)
String json = """
{"name": "%s", "email": "%s"}
""".formatted(name, email);---
Error Handling
| Type | When | Examples | |------|------|---------| | Checked | Recoverable I/O the caller must handle | IOException, SQLException | | Unchecked | Programming errors, business rule violations | IllegalArgumentException, custom domain exceptions |
Custom Exception Hierarchy
public abstract class DomainException extends RuntimeException {
private final String errorCode;
protected DomainException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public String errorCode() { return errorCode; }
}
public class EntityNotFoundException extends DomainException {
public EntityNotFoundException(String entity, Object id) {
super("NOT_FOUND", "%s with id %s not found".formatted(entity, id));
}
}Try-With-Resources
try (var conn = dataSource.getConnection();
var stmt = conn.prepareStatement(sql);
var rs = stmt.executeQuery()) {
while (rs.next()) { results.add(mapRow(rs)); }
}Global Handler (Spring)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.errorCode(), ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
var errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage()).toList();
return ResponseEntity.badRequest().body(new ErrorResponse("VALIDATION_ERROR", errors.toString()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
log.error("Unhandled exception", ex);
return ResponseEntity.internalServerError()
.body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
}
}
public record ErrorResponse(String code, String message) {}---
Testing Patterns
JUnit 5 + Mockito + AssertJ
@DisplayName("UserService")
class UserServiceTest {
private UserRepository repository;
private UserService service;
@BeforeEach
void setUp() {
repository = mock(UserRepository.class);
service = new UserService(repository);
}Read more
name: java-patterns description: "Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class." effort: medium user-invocable: false allowed-tools: Read
Java Patterns Skill
Project Structure
Maven / Gradle Standard Layout
my-app/ ├── pom.xml (or build.gradle.kts + settings.gradle.kts) ├── src/ │ ├── main/ │ │ ├── java/com/example/myapp/ │ │ │ ├── MyApplication.java │ │ │ ├── config/ │ │ │ ├── controller/ │ │ │ ├── service/ │ │ │ ├── repository/ │ │ │ ├── model/ │ │ │ │ ├── entity/ │ │ │ │ └── dto/ │ │ │ └── exception/ │ │ └── resources/ │ │ ├── application.yml │ │ └── db/migration/ │ └── test/ │ ├── java/com/example/myapp/ │ └── resources/application-test.yml └── target/ (or build/)
Multi-Module
parent/ ├── pom.xml (packaging=pom) ├── common/ (shared utilities) ├── domain/ (entities, business rules) ├── api/ (REST controllers, DTOs) └── app/ (Spring Boot main, wiring)
---
Idioms / Code Style
Records (Java 16+)
public record UserDto(Long id, String name, String email) {
public UserDto { // compact constructor for validation
Objects.requireNonNull(name, "name must not be null");
Objects.requireNonNull(email, "email must not be null");
}
}Sealed Classes (Java 17+)
public sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}
public record Circle(double radius) implements Shape {
public double area() { return Math.PI * radius * radius; }
}
public record Rectangle(double w, double h) implements Shape {
public double area() { return w * h; }
}
public record Triangle(double base, double height) implements Shape {
public double area() { return 0.5 * base * height; }
}Switch Expressions (Java 14+)
String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle r=" + c.radius();
case Rectangle r -> "Rect %sx%s".formatted(r.w(), r.h());
case Triangle t -> "Triangle base=" + t.base();
};
}
// Guard patterns (Java 21+)
String classify(Shape shape) {
return switch (shape) {
case Circle c when c.radius() > 100 -> "large circle";
case Circle c -> "small circle";
case Rectangle r -> "rectangle";
case Triangle t -> "triangle";
};
}var, Streams, Optional
// var -- use when RHS makes type obvious
var users = new ArrayList<User>();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Avoid: var result = service.process(data); -- type unclear
// Streams
List<String> names = users.stream()
.filter(User::isActive)
.map(User::name)
.sorted()
.toList(); // Java 16+, unmodifiable
Map<Department, List<User>> byDept = users.stream()
.collect(Collectors.groupingBy(User::department));
// Optional -- return type only, never as field or parameter
String city = findByEmail(email)
.map(User::address)
.map(Address::city)
.orElse("Unknown");
// Never call .get() without guard -- use orElse/orElseThrow
// Text blocks (Java 15+)
String json = """
{"name": "%s", "email": "%s"}
""".formatted(name, email);---
Error Handling
| Type | When | Examples | |------|------|---------| | Checked | Recoverable I/O the caller must handle | IOException, SQLException | | Unchecked | Programming errors, business rule violations | IllegalArgumentException, custom domain exceptions |
Custom Exception Hierarchy
public abstract class DomainException extends RuntimeException {
private final String errorCode;
protected DomainException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public String errorCode() { return errorCode; }
}
public class EntityNotFoundException extends DomainException {
public EntityNotFoundException(String entity, Object id) {
super("NOT_FOUND", "%s with id %s not found".formatted(entity, id));
}
}Try-With-Resources
try (var conn = dataSource.getConnection();
var stmt = conn.prepareStatement(sql);
var rs = stmt.executeQuery()) {
while (rs.next()) { results.add(mapRow(rs)); }
}Global Handler (Spring)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.errorCode(), ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
var errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage()).toList();
return ResponseEntity.badRequest().body(new ErrorResponse("VALIDATION_ERROR", errors.toString()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
log.error("Unhandled exception", ex);
return ResponseEntity.internalServerError()
.body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
}
}
public record ErrorResponse(String code, String message) {}---
Testing Patterns
JUnit 5 + Mockito + AssertJ
@DisplayName("UserService")
class UserServiceTest {
private UserRepository repository;
private UserService service;
@BeforeEach
void setUp() {
repository = mock(UserRepository.class);
service = new UserService(repository);
}Professional-grade AI coding toolkit with multi-platform support. Machine-enforced safety, 109 skills, 44 agents, expanded lifecycle hooks, persona presets, experimental opt-in plugin packs, and benchmark tooling — works with Claude Code, Claude Chat/Cowork,
Repo: softspark/ai-toolkit
Other skills on ai-toolkit.
- /ai-toolkit-rules
Mandatory engineering, security, testing, git, performance, quality, and response rules. Claude MUST load this skill for every technical, coding, debugging, review, architecture, DevOps, data, or file-editing task in Chat or Cowork.
Open skill - /mem-search
Search past coding sessions using natural language. Finds relevant observations, decisions, and context from previous work.
Open skill - /a11y-validate
Accessibility validator: WCAG 2.1 AA, EN 301 549, EAA. Triggers: a11y, accessibility, WCAG, EAA, ARIA, contrast, keyboard, screen reader.
Open skill - /agent-creator
Creates new specialized agents with frontmatter, tools, delegation. Triggers: new agent, create agent, agent scaffold, specialized agent.
Open skill - /analyze
Analyzes code quality, complexity, patterns across codebase. Triggers: quality report, hotspot scan, code analysis, architecture signal.
Open skill - /api-patterns
REST/GraphQL API design: naming, versioning, pagination, idempotency, OpenAPI. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, idempotency, rate limit.
Open skill

