JavaSpring顾问

khalilbenaz2026-10-28查看 1346复制 1402

使用 Spring Boot、Spring Security 和 Spring 生态系统的 Java 开发。会在“Java”、“Spring Boot”、“Spring”、“JPA”、“Hibernate”、“Maven”、“Gradle”、“Spring Security”、“microservices Java”时触发。也会在“Java bean configuration”时触发。

Download (.md)

📘如何使用这个技能

  1. 1
    复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
  2. 2
    打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
  3. 3
    粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
  4. 4
    直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景

💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。

SKILL.md9207 chars
---
name: java-spring-advisor
description: Développement Java avec Spring Boot, Spring Security et l'écosystème Spring. Se déclenche avec "Java", "Spring Boot", "Spring", "JPA", "Hibernate", "Maven", "Gradle", "Spring Security", "microservices Java". Also triggers on "Java bean configuration".
---

# Java Spring Advisor

## Workflow

### 1. Initialisation du projet

```bash
# Spring Initializr CLI (Spring Boot 3.x, Java 21)
curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.3.0 \
  -d groupId=com.example \
  -d artifactId=myapp \
  -d javaVersion=21 \
  -d dependencies=web,data-jpa,security,actuator,validation,flyway \
  -o myapp.zip && unzip myapp.zip
```

Structure des profils :
```
src/main/resources/
  application.yml          # valeurs communes
  application-dev.yml      # dev local (H2, logs DEBUG)
  application-prod.yml     # prod (connexion pool, logs INFO)
```

Typer la configuration avec `@ConfigurationProperties` plutôt que `@Value` :
```java
@ConfigurationProperties(prefix = "app.payment")
public record PaymentProperties(String apiUrl, Duration timeout, int maxRetries) {}
```

### 2. Architecture — Critères de choix

| Contexte | Approche recommandée |
|---|---|
| API CRUD simple | Couches Controller → Service → Repository |
| Domaine métier riche | Hexagonale (ports/adapters) + DDD |
| Gros projet (>5 équipes) | Multi-modules Maven par bounded context |
| Microservices | 1 module = 1 deployable, API contract-first OpenAPI |

Organisation par **feature** (pas par couche technique) dans les projets moyens/grands :
```
com.example.payment/
  PaymentController.java
  PaymentService.java
  PaymentRepository.java
  PaymentDto.java         # record Java 21
  Payment.java            # entité JPA
```

### 3. Data access — JPA & transactions

**Problème N+1** — détecter et corriger :
```java
// Mauvais : génère N+1 requêtes
List<Order> orders = orderRepo.findAll();
orders.forEach(o -> o.getItems().size()); // lazy load en boucle

// Correct : fetch join explicite
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :status")
List<Order> findWithItems(@Param("status") OrderStatus status);

// Ou via @EntityGraph
@EntityGraph(attributePaths = {"items", "customer"})
List<Order> findByStatus(OrderStatus status);
```

Règles `@Transactional` :
- Toujours sur la méthode `public` du Service, jamais sur le Repository
- `@Transactional(readOnly = true)` sur les lectures → flush mode OFF + optimisations Hibernate
- Propagation `REQUIRES_NEW` uniquement si isolation réelle nécessaire (audit, log d'erreur)

Migration de schéma avec Flyway :
```
db/migration/
  V1__create_orders.sql
  V2__add_payment_status.sql
```

### 4. REST API

Handler d'erreur global :
```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(EntityNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ProblemDetail handleNotFound(EntityNotFoundException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        var pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        pd.setProperty("errors", ex.getBindingResult().getFieldErrors()
            .stream().map(e -> e.getField() + ": " + e.getDefaultMessage()).toList());
        return pd;
    }
}
```

DTO avec record Java 21 + validation :
```java
public record CreateOrderRequest(
    @NotBlank String customerId,
    @NotEmpty List<@Valid OrderItemRequest> items,
    @PositiveOrZero BigDecimal discount
) {}
```

Documentation OpenAPI (SpringDoc) :
```yaml
# application.yml
springdoc:
  api-docs.path: /api-docs
  swagger-ui.path: /swagger-ui.html
  show-actuator: false
```

### 5. Spring Security (Spring Boot 3.x)

Configuration minimale JWT Resource Server :
```java
@Configuration
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health").permitAll()
                .anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
    }
}
```

Sécurisation par méthode :
```java
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
public UserDto getUser(String userId) { ... }
```

**CORS** : configurer via `CorsConfigurationSource` bean, pas via `@CrossOrigin` sur chaque contrôleur.

### 6. Messaging

RabbitMQ — pattern listener avec retry :
```java
@RabbitListener(queues = "payment.queue",
    containerFactory = "retryContainerFactory")
public void handlePayment(PaymentEvent event) {
    paymentService.process(event);
}
```

Kafka — configuration producer idempotent :
```yaml
spring.kafka.producer:
  enable-idempotence: true
  acks: all
  retries: 3
```

Dead-letter queue : configurer `x-dead-letter-exchange` sur la queue principale + consumer séparé sur la DLQ pour replay manuel.

### 7. Testing — Pyramide

```
E2E (WireMock + Testcontainers)  ← peu nombreux, lents
Integration (@SpringBootTest)     ← couverture des flux critiques
Slice (@WebMvcTest, @DataJpaTest) ← rapides, ciblés
Unit (JUnit 5 + Mockito)          ← majoritaires
```

Test slice REST :
```java
@WebMvcTest(OrderController.class)
class OrderControllerTest {

    @Autowired MockMvc mvc;
    @MockBean OrderService orderService;

    @Test
    void createOrder_returns201() throws Exception {
        given(orderService.create(any())).willReturn(new OrderDto("123"));
        mvc.perform(post("/orders")
                .contentType(APPLICATION_JSON)
                .content("""{"customerId":"C1","items":[]}"""))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value("123"));
    }
}
```

Testcontainers (PostgreSQL) :
```java
@Testcontainers
@SpringBootTest
class OrderRepositoryIT {

    @Container
    static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16");

    @DynamicPropertySource
    static void props(DynamicPropertyRegistry r) {
        r.add("spring.datasource.url", pg::getJdbcUrl);
        r.add("spring.datasource.username", pg::getUsername);
        r.add("spring.datasource.password", pg::getPassword);
    }
}
```

### 8. Observabilité

```yaml
management:
  endpoints.web.exposure.include: health,info,prometheus,metrics
  endpoint.health.show-details: when-authorized
  metrics.export.prometheus.enabled: true
```

Corréler logs + traces :
```java
// Micrometer Tracing auto-injecte traceId/spanId dans MDC si Zipkin/OTLP est configuré
// Ajouter dans logback-spring.xml :
// %X{traceId} %X{spanId}
```

Virtual threads (Java 21 + Spring Boot 3.2+) pour améliorer le throughput I/O-bound :
```yaml
spring.threads.virtual.enabled: true
```

---

## Anti-patterns & pièges

| Piège | Conséquence | Correction |
|---|---|---|
| `@Autowired` sur champ | Tests unitaires impossibles sans contexte Spring | Injection par constructeur (`final` + `@RequiredArgsConstructor`) |
| `@Transactional` sur méthode `private` | Transaction silencieusement ignorée (proxy CGLIB) | Déplacer sur méthode `public` ou extraire dans un autre bean |
| Appel interne `this.method()` transactionnel | Bypass du proxy → pas de transaction | Injecter le bean lui-même ou restructurer |
| Entité JPA exposée dans le Controller | Couplage fort, risque de lazy init exception | Toujours mapper vers DTO avant de sérialiser |
| `FetchType.EAGER` par défaut sur `@OneToMany` | Full table join à chaque chargement | Garder `LAZY` + fetch join explicite quand nécessaire |
| `@SpringBootTest` pour tout | Suite de tests lente (contexte complet) | Préférer `@WebMvcTest` / `@DataJpaTest` slices |
| Secrets dans `application.yml` commités | Fuite de credentials | Variables d'environnement ou Spring Cloud Config / Vault |
| Ignorer `readOnly = true` | Flush inutile + dirty checking sur toutes les entités | `@Transactional(readOnly = true)` sur toutes les méthodes de lecture |

## Bonnes pratiques 2026

- **Java 21 LTS** : records pour DTOs/Value Objects, sealed classes pour hiérarchies fermées, pattern matching dans les `switch`, virtual threads activés par défaut en Spring Boot 3.2+.
- **Spring Boot 3.3.x** : `ProblemDetail` (RFC 7807) pour les erreurs REST, `@HttpExchange` pour les clients HTTP déclaratifs (remplace Feign dans les nouveaux projets).
- **GraalVM Native Image** : vérifier la compatibilité des librairies avec `spring-aot-maven-plugin`; tester en CI avec `./mvnw -Pnative test`.
- **Observabilité** : préférer OpenTelemetry (OTLP) à Zipkin pour ne pas se lier à un backend; Micrometer 1.12+ supporte OTLP nativement.
- **Sécurité** : scanner régulièrement les dépendances avec `./mvnw dependency-check:check` (OWASP) ou Dependabot; ne jamais logger les JWT ni les payloads contenant des PII.
来源:skillsdirectory

评论0

登录后参与评论

还没有评论,来抢沙发吧