用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/oimiragieo/agent-studio --skill java-expert命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | java-expert |
| description | Java and Spring Boot expert including REST APIs, JPA, and microservices |
| version | 1.1.0 |
| model | sonnet |
| invoked_by | both |
| user_invocable | true |
| tools | ["Read","Write","Edit","Bash","Grep","Glob"] |
| consolidated_from | 1 skills |
| best_practices | ["Follow domain-specific conventions","Apply patterns consistently","Prioritize type safety and testing"] |
| error_handling | graceful |
| streaming | supported |
| verified | true |
| lastVerifiedAt | "2026-02-22T00:00:00.000Z" |
| source | builtin |
| trust_score | 100 |
| provenance_sha | 3f805f4c1efbab8e |
Virtual Threads (Project Loom)
Executors.newVirtualThreadPerTaskExecutor() for thread pools// 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
String result = switch (obj) {
case String s -> "String: " + s;
case Integer i -> "Integer: " + i;
Long l -> + l;
-> ;
-> ;
};
{}
(obj ) {
System.out.println( + x + + y);
}
Example usage:
```
User: "Review this code for java best practices"
Agent: [Analyzes code against consolidated guidelines and provides specific feedback]
```
Records
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
public sealed interface Result<T> permits Success, Failure {
record Success<T>(T value) implements Result<T> {}
record Failure<T>(String error) implements Result<T> {}
}
Framework Setup:
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):
@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:
@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):
JpaRepository<Entity, ID>@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();
}
Entity Design:
@Entity and @Table annotations@Id with generation strategy@Column for constraints and mappingsequals() 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:
@EntityGraph or JOIN FETCH to prevent N+1 queries@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);
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", "john@example.com");
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(dto)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("John Doe"));
}
}
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'
}
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 (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
@Autowired — field injection hides dependencies, makes testing harder, and creates partially-initialized objects that crash at runtime if the context isn't fully loaded.Optional.get() without a preceding isPresent() check or orElse()/orElseThrow() — unconditional get() throws NoSuchElementException on empty optionals, silently defeating Optional's entire purpose.@Transactional boundaries explicitly — calling a transactional method from within the same class bypasses the proxy and runs without a transaction, causing silent data inconsistency.@Async without a configured TaskExecutor — Spring's default @Async executor uses a single-thread pool; concurrent async calls queue up and defeat parallelism.@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-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 |
This expert skill consolidates 1 individual skills:
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.