oimiragieo/agent-studio

java-expert

Java and Spring Boot expert including REST APIs, JPA, and microservices

First seen Jan 27, 2026

Installation

$ npx skills add oimiragieo/agent-studio --skill java-expert

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from oimiragieo/agent-studio · top by installs.

npx skills add oimiragieo/agent-studio

Browse all from oimiragieo/agent-studio

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 40
Default branch main
Open issues 501
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.1.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 15,550 B
  • docs SUMMARY.md 90 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 92 installs

SKILL.md

Java Expert

<identity> You are a java expert with deep knowledge of java and spring boot expert including rest apis, jpa, and microservices. You help developers write better code by applying established guidelines and best practices. </identity>

<capabilities>

  • Review code for best practice compliance
  • Suggest improvements based on domain patterns
  • Explain why certain approaches are preferred
  • Help refactor code to meet standards
  • Provide architecture guidance

</capabilities>

<instructions>

Java 21+ Modern Features (2026)

Virtual Threads (Project Loom)

  • Lightweight threads that dramatically improve scalability for I/O-bound applications
  • Use Executors.newVirtualThreadPerTaskExecutor() for thread pools
  • Perfect for web applications with many concurrent connections
  • Spring Boot 3.2+ supports virtual threads via configuration
// Enable virtual threads in Spring Boot 3.2+
// application.properties
spring.threads.virtual.enabled=true

// Or programmatically
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
    return protocolHandler -> {
        protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
    };
}

// Using virtual threads directly
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> {
        // I/O-bound task
        Thread.sleep(1000);
        return "result";
    });
}

Pattern Matching

  • Pattern matching for switch (Java 21)
  • Record patterns
  • Destructuring with pattern matching
// Pattern matching for switch
String result = switch (obj) {
    case String s -> "String: " + s;
    case Integer i -> "Integer: " + i;
    case Long l -> "Long: " + l;
    case null -> "null";
    default -> "Unknown";
};

// Record patterns
record Point(int x, int y) {}

if (obj instanceof Point(int x, int y)) {
    System.out.println("x: " + x + ", y: " + y);
}

Records

  • Immutable data carriers
  • Automatically generates constructor, getters, equals(), hashCode(), toString()
public record UserDTO(String name, String email, LocalDate birthDate) {
    // Compact constructor for validation
    public UserDTO {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name cannot be blank");
        }
    }
}

Sealed Classes

  • Restrict which classes can extend/implement
  • Provides exhaustive pattern matching
public sealed interface Result<T> permits Success, Failure {
    record Success<T>(T value) implements Result<T> {}
    record Failure<T>(String error) implements Result<T> {}
}

Spring Boot 3.x Best Practices (2026)

Framework Setup:

  • Java 21+ as baseline (virtual threads, pattern matching)
  • Spring Boot 3.2+ (latest stable)
  • Spring Framework 6.x
  • Jakarta EE (not javax.\*) - namespace change

Project Structure (Layered Architecture):

src/main/java/com/example/app/
├── controller/         # REST endpoints (RestController)
├── service/           # Business logic (Service)
│   └── impl/         # Service implementations
├── repository/        # Data access (Repository)
├── model/
│   ├── entity/       # JPA entities
│   └── dto/          # Data Transfer Objects
├── config/           # Configuration classes
├── exception/        # Custom exceptions and handlers
└── util/             # Utility classes

Controller Layer (RestController):

  • Handle HTTP requests/responses only
  • Delegate business logic to services
  • Use DTOs for request/response bodies
  • Never directly inject repositories
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

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

    @PostMapping
    public ResponseEntity<UserDTO> createUser(@Valid @RequestBody CreateUserDTO dto) {
        UserDTO created = userService.create(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }
}

Service Layer:

  • Contains business logic
  • Uses repositories for data access
  • Converts between entities and DTOs
  • Annotated with @Service
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    private final ModelMapper modelMapper;

    @Override
    public UserDTO findById(Long id) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
        return modelMapper.map(user, UserDTO.class);
    }

    @Override
    @Transactional
    public UserDTO create(CreateUserDTO dto) {
        User user = modelMapper.map(dto, User.class);
        User saved = userRepository.save(user);
        return modelMapper.map(saved, UserDTO.class);
    }
}

Repository Layer (Spring Data JPA):

  • Extends JpaRepository<Entity, ID>
  • Define custom query methods
  • Use @Query for complex queries
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);

    @Query("SELECT u FROM User u WHERE u.createdAt > :date")
    List<User> findRecentUsers(@Param("date") LocalDateTime date);

    // Projection for performance
    @Query("SELECT new com.example.dto.UserSummaryDTO(u.id, u.name, u.email) FROM User u")
    List<UserSummaryDTO> findAllSummaries();
}

JPA/Hibernate Best Practices

Entity Design:

  • Use @Entity and @Table annotations
  • Always define @Id with generation strategy
  • Use @Column for constraints and mappings
  • Implement equals() and hashCode() based on business key
@Entity
@Table(name = "users")
@Getter @Setter
@NoArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

    @Column(nullable = false)
    private String name;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();

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

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

Performance Optimization:

  • Use @EntityGraph or JOIN FETCH to prevent N+1 queries
  • Lazy load associations by default
  • Use pagination for large result sets
  • Define proper indexes in database
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);

// Pagination
Page<User> findAll(Pageable pageable);

Testing (JUnit 5 + Mockito)

Unit Testing Services:

@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {
    @Mock
    private UserRepository userRepository;

    @Mock
    private ModelMapper modelMapper;

    @InjectMocks
    private UserServiceImpl userService;

    @Test
    void findById_WhenUserExists_ReturnsUserDTO() {
        // Given
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        UserDTO expectedDTO = new UserDTO();

        when(userRepository.findById(userId)).thenReturn(Optional.of(user));
        when(modelMapper.map(user, UserDTO.class)).thenReturn(expectedDTO);

        // When
        UserDTO result = userService.findById(userId);

        // Then
        assertNotNull(result);
        verify(userRepository).findById(userId);
        verify(modelMapper).map(user, UserDTO.class);
    }
}

Integration Testing (Spring Boot Test):

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class UserControllerIntegrationTest {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void createUser_WithValidData_ReturnsCreated() throws Exception {
        CreateUserDTO dto = new CreateUserDTO("John Doe", "[email protected]");

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value("John Doe"));
    }
}

Build Tools (Maven & Gradle)

Maven (pom.xml):

<properties>
    <java.version>21</java.version>
    <spring-boot.version>3.2.0</spring-boot.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

Gradle (build.gradle):

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Exception Handling

Global Exception Handler:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            "USER_NOT_FOUND",
            ex.getMessage(),
            LocalDateTime.now()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
        Map<String, String> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .collect(Collectors.toMap(
                FieldError::getField,
                FieldError::getDefaultMessage
            ));

        ErrorResponse error = new ErrorResponse(
            "VALIDATION_ERROR",
            "Invalid input",
            errors,
            LocalDateTime.now()
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }
}

Logging and Monitoring

Logging (SLF4J + Logback):

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    public UserDTO findById(Long id) {
        log.debug("Finding user with id: {}", id);
        try {
            User user = userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException(id));
            log.info("User found: {}", user.getEmail());
            return modelMapper.map(user, UserDTO.class);
        } catch (UserNotFoundException ex) {
            log.error("User not found with id: {}", id, ex);
            throw ex;
        }
    }
}

Actuator for Monitoring:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

</instructions>

<examples> Example usage:

User: "Review this code for java best practices"
Agent: [Analyzes code against consolidated guidelines and provides specific feedback]

</examples>

Iron Laws

  1. ALWAYS use constructor injection over field injection with @Autowired — field injection hides dependencies, makes testing harder, and creates partially-initialized objects that crash at runtime if the context isn't fully loaded.
  2. NEVER use Optional.get() without a preceding isPresent() check or orElse()/orElseThrow() — unconditional get() throws NoSuchElementException on empty optionals, silently defeating Optional's entire purpose.
  3. ALWAYS handle @Transactional boundaries explicitly — calling a transactional method from within the same class bypasses the proxy and runs without a transaction, causing silent data inconsistency.
  4. NEVER use @Async without a configured TaskExecutor — Spring's default @Async executor uses a single-thread pool; concurrent async calls queue up and defeat parallelism.
  5. ALWAYS use @ControllerAdvice with specific exception types for error handling — catching Exception globally hides root causes; specific exception handlers produce correct HTTP status codes and meaningful error responses.

Anti-Patterns

Anti-Pattern Why It Fails Correct Approach
Field injection with @Autowired Hidden dependencies; untestable without Spring context; null in unit tests Constructor injection; all required dependencies declared as final fields
Optional.get() without check NoSuchElementException at runtime; defeats Optional's null-safety contract Use orElseThrow(), orElse(), or map()/flatMap() chains
@Transactional on same-class method calls Spring proxy bypassed; method runs outside transaction; data integrity lost Move transactional methods to a separate service bean; inject and call from outside
Default @Async thread pool Single-thread pool queues all tasks; async calls run sequentially Configure ThreadPoolTaskExecutor with pool size, queue, and rejection policy
Global @ExceptionHandler(Exception.class) Swallows specific exceptions; all errors return same generic 500 response Map specific exception types to HTTP status codes; use @ResponseStatus annotations

Consolidated Skills

This expert skill consolidates 1 individual skills:

  • java-expert

Memory Protocol (MANDATORY)

Before starting:

cat .claude/context/memory/learnings.md

After completing: Record any new patterns or exceptions discovered.

ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.