moai-lang-java

Java 21 LTS development specialist covering Spring Boot 3.3, virtual threads, pattern matching, and enterprise patterns. Use when building enterprise applications, microservices, or Spring projects.

$ 安裝

git clone https://github.com/modu-ai/moai-adk /tmp/moai-adk && cp -r /tmp/moai-adk/.claude/skills/moai-lang-java ~/.claude/skills/moai-adk

// tip: Run this command in your terminal to install the skill


name: "moai-lang-java" description: "Java 21 LTS development specialist covering Spring Boot 3.3, virtual threads, pattern matching, and enterprise patterns. Use when building enterprise applications, microservices, or Spring projects." version: 1.0.0 category: "language" modularized: false tags: ['java', 'spring-boot', 'jpa', 'hibernate', 'virtual-threads', 'enterprise'] context7-libraries: ['/spring-projects/spring-boot', '/spring-projects/spring-framework', '/spring-projects/spring-security'] related-skills: ['moai-lang-kotlin', 'moai-domain-backend'] updated: 2025-12-07 status: "active"

Quick Reference (30 seconds)

Java 21 LTS Expert - Enterprise development with Spring Boot 3.3, Virtual Threads, and modern Java features.

Auto-Triggers: Java files (.java), build files (pom.xml, build.gradle, build.gradle.kts)

Core Capabilities:

  • Java 21 LTS: Virtual threads, pattern matching, record patterns, sealed classes
  • Spring Boot 3.3: REST controllers, services, repositories, WebFlux reactive
  • Spring Security 6: JWT authentication, OAuth2, role-based access control
  • JPA/Hibernate 7: Entity mapping, relationships, queries, transactions
  • JUnit 5: Unit testing, mocking, TestContainers integration
  • Build Tools: Maven 3.9, Gradle 8.5 with Kotlin DSL

Implementation Guide (5 minutes)

Java 21 LTS Features

Virtual Threads (Project Loom):

// Lightweight concurrent programming
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i ->
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        })
    );
}

// Structured concurrency (preview)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Supplier<User> user = scope.fork(() -> fetchUser(userId));
    Supplier<List<Order>> orders = scope.fork(() -> fetchOrders(userId));
    scope.join().throwIfFailed();
    return new UserWithOrders(user.get(), orders.get());
}

Pattern Matching for Switch:

String describe(Object obj) {
    return switch (obj) {
        case Integer i when i > 0 -> "positive integer: " + i;
        case Integer i -> "non-positive integer: " + i;
        case String s -> "string of length " + s.length();
        case List<?> list -> "list with " + list.size() + " elements";
        case null -> "null value";
        default -> "unknown type";
    };
}

Record Patterns and Sealed Classes:

record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}

int area(Rectangle rect) {
    return switch (rect) {
        case Rectangle(Point(var x1, var y1), Point(var x2, var y2)) ->
            Math.abs((x2 - x1) * (y2 - y1));
    };
}

public sealed interface Shape permits Circle, Rectangle {
    double area();
}
public record Circle(double radius) implements Shape {
    public double area() { return Math.PI * radius * radius; }
}

Spring Boot 3.3

REST Controller:

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<UserDto> createUser(@Valid @RequestBody CreateUserRequest request) {
        UserDto user = userService.create(request);
        URI location = URI.create("/api/users/" + user.id());
        return ResponseEntity.created(location).body(user);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        return userService.delete(id)
            ? ResponseEntity.noContent().build()
            : ResponseEntity.notFound().build();
    }
}

Service Layer:

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }

    @Transactional
    public User create(CreateUserRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new DuplicateEmailException(request.email());
        }
        var user = User.builder()
            .name(request.name())
            .email(request.email())
            .passwordHash(passwordEncoder.encode(request.password()))
            .status(UserStatus.PENDING)
            .build();
        return userRepository.save(user);
    }
}

Spring Security 6

Security Configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .csrf(csrf -> csrf.disable())
            .build();
    }

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

JPA/Hibernate Patterns

Entity Definition:

@Entity
@Table(name = "users")
@Getter @Setter
@NoArgsConstructor @Builder
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

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

    @Enumerated(EnumType.STRING)
    private UserStatus status;

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

Repository with Custom Queries:

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    boolean existsByEmail(String email);

    @Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id")
    Optional<User> findByIdWithOrders(@Param("id") Long id);

    Page<User> findByNameContainingIgnoreCase(String name, Pageable pageable);
}

DTOs as Records:

public record UserDto(Long id, String name, String email, UserStatus status) {
    public static UserDto from(User user) {
        return new UserDto(user.getId(), user.getName(), user.getEmail(), user.getStatus());
    }
}

public record CreateUserRequest(
    @NotBlank @Size(min = 2, max = 100) String name,
    @NotBlank @Email String email,
    @NotBlank @Size(min = 8) String password
) {}

Advanced Patterns

Virtual Threads Integration

@Service
@RequiredArgsConstructor
public class AsyncUserService {
    public UserWithDetails fetchUserDetails(Long userId) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Supplier<User> userTask = scope.fork(() -> userRepo.findById(userId).orElseThrow());
            Supplier<List<Order>> ordersTask = scope.fork(() -> orderRepo.findByUserId(userId));
            scope.join().throwIfFailed();
            return new UserWithDetails(userTask.get(), ordersTask.get());
        }
    }

    public void processUsersInParallel(List<Long> userIds) {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            userIds.stream().map(id -> executor.submit(() -> processUser(id))).toList();
        }
    }
}

Build Configuration

Maven 3.9:

<project>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
    </parent>
    <properties><java.version>21</java.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>
    </dependencies>
</project>

Gradle 8.5 (Kotlin DSL):

plugins {
    id("org.springframework.boot") version "3.3.0"
    id("io.spring.dependency-management") version "1.1.4"
    java
}
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Testing with JUnit 5

Unit Testing:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock private UserRepository userRepository;
    @InjectMocks private UserService userService;

    @Test
    void shouldCreateUser() {
        when(userRepository.existsByEmail(anyString())).thenReturn(false);
        when(userRepository.save(any())).thenReturn(User.builder().id(1L).build());
        var result = userService.create(new CreateUserRequest("John", "john@test.com", "pass"));
        assertThat(result.getId()).isEqualTo(1L);
    }
}

Integration Testing with TestContainers:

@Testcontainers
@SpringBootTest
class UserRepositoryTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");

    @DynamicPropertySource
    static void props(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
    }

    @Autowired UserRepository repo;

    @Test
    void shouldSaveUser() {
        var user = repo.save(User.builder().name("John").email("john@test.com").build());
        assertThat(user.getId()).isNotNull();
    }
}

Context7 Integration

Library mappings for latest documentation:

  • /spring-projects/spring-boot - Spring Boot 3.3 documentation
  • /spring-projects/spring-framework - Spring Framework core
  • /spring-projects/spring-security - Spring Security 6
  • /hibernate/hibernate-orm - Hibernate 7 ORM patterns
  • /junit-team/junit5 - JUnit 5 testing framework

Works Well With

  • moai-lang-kotlin - Kotlin interoperability and Spring Kotlin extensions
  • moai-domain-backend - REST API, GraphQL, microservices architecture
  • moai-domain-database - JPA, Hibernate, R2DBC patterns
  • moai-foundation-quality - JUnit 5, Mockito, TestContainers integration
  • moai-infra-docker - JVM container optimization

Troubleshooting

Common Issues:

  • Version mismatch: java -version, check JAVA_HOME points to Java 21
  • Compilation errors: mvn clean compile -X or gradle build --info
  • Virtual thread issues: Ensure Java 21+ with --enable-preview if needed
  • JPA lazy loading: Use @Transactional or JOIN FETCH queries

Performance Tips:

  • Enable Virtual Threads: spring.threads.virtual.enabled=true
  • Use GraalVM Native Image for faster startup
  • Configure connection pooling with HikariCP

Advanced Documentation

For comprehensive reference materials:

  • reference.md - Java 21 features, Context7 mappings, performance
  • examples.md - Production-ready Spring Boot examples

Last Updated: 2025-12-07 Status: Production Ready (v1.0.0)