Skip to content
Back to skills

Spring Framework Advanced Skill

ASecurity

Deep Spring Framework knowledge beyond Spring Boot basics. Covers IoC container internals, AOP, WebFlux/reactive, Spring Batch, Spring Cloud, Security, Events, custom auto-configuration, and debugging. Used by coding and health inspection agents.

  • 3 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 5, 2026
developmentgojavareactspringdebuggingapidatabasesecurityperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 21 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add sharmapuneet1510/awesome-prompts --skill skills --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Spring Framework Advanced Skill?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Spring Framework Advanced Skill
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sharmapuneet1510-spring-framework-advanced-skill/badge)](https://www.skillsdirectory.com/skills/sharmapuneet1510-spring-framework-advanced-skill)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Spring Framework Advanced Skill
version: 1.1
description: >
  Deep Spring Framework knowledge beyond Spring Boot basics. Covers IoC container
  internals, AOP, WebFlux/reactive, Spring Batch, Spring Cloud, Security, Events,
  custom auto-configuration, and debugging. Used by coding and health inspection agents.
applies_to: [java, spring, spring-boot, spring-webflux, spring-batch, spring-cloud, spring-security]
tags: [spring, aop, webflux, reactive, batch, cloud, security, ioc]
---

# Spring Framework Advanced Skill — v1.1

## Quick Card

> Read this card first. Load a section below only when the task needs it.

| | |
|---|---|
| **Use when** | Spring internals: IoC, AOP, WebFlux, Batch, Cloud, events, or a Spring behaviour that makes no sense |
| **Skip when** | Plain Spring Boot CRUD — `java_advanced_skill` §4 |
| **Inputs** | Spring Boot version, the misbehaving bean or config |
| **Produces** | Correct bean wiring, aspects, reactive pipelines, batch jobs, event flows |
| **Steps** | 1. Identify the mechanism (proxy, scope, context) → 2. Load the matching section → 3. Apply → 4. Verify with a slice or context test |
| **Done when** | Behaviour explained by mechanism, not by trial and error; test covers it |
| **Senior defaults** | Proxies explain most surprises — self-invocation skips `@Transactional`/`@Async`; `private`/`final` methods are never advised · `@TransactionalEventListener(AFTER_COMMIT)` for side effects · never block on a reactive thread · prefer `@ConfigurationProperties` records over `@Value` scatter |
| **Load on demand** | §1 IoC · §2 AOP · §3 WebFlux · §4 Batch · §5 Cloud · §6 events · §7 debugging · §8 quick reference |
| **Run report** | `html_report_skill` — adds: Mechanism identified |
| **Pairs with** | `java_advanced_skill`, `lombok_skill`, `opentelemetry_skill` |

---

## 1. IoC Container — How It Really Works

Understanding the container lifecycle helps debug startup failures and bean wiring issues.

```java
Application starts
  → Reads @ComponentScan / @Bean / XML config
  → Creates BeanDefinition for each bean (metadata — not the instance yet)
  → Resolves dependencies between BeanDefinitions
  → Instantiates beans in dependency order
  → Injects dependencies (constructor → setter → field in that order)
  → Calls @PostConstruct methods
  → Application is ready

Application shuts down
  → Calls @PreDestroy methods
  → Destroys singletons in reverse creation order
```

### Bean Scopes

```java
@Component
@Scope("singleton")    // default — one instance per application context
public class OrderService { }

@Component
@Scope("prototype")    // new instance every time it is requested from the context
public class OrderProcessor { }

// Web scopes — only valid in a web application context
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext { }   // new instance per HTTP request

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession { }      // one instance per HTTP session
```

### Conditional Beans

```java
/**
 * Creates the notification sender appropriate for the current environment.
 *
 * <p>In production: real email sender.
 * In test/dev: a stub that just logs — no real emails sent.</p>
 */
@Configuration
public class NotificationConfig {

    /**
     * Real email sender — only active when spring.mail.host is configured.
     * This means it won't try to connect to an SMTP server in dev/test.
     */
    @Bean
    @ConditionalOnProperty(name = "spring.mail.host")
    public NotificationSender emailNotificationSender(JavaMailSender mailSender) {
        return new EmailNotificationSender(mailSender);
    }

    /**
     * Stub sender — used when no mail host is configured.
     * Logs the notification instead of sending it.
     */
    @Bean
    @ConditionalOnMissingBean(NotificationSender.class)
    public NotificationSender stubNotificationSender() {
        return (recipient, subject, message) -> {
            log.info("[STUB] Notification to {}: {}", recipient, subject);
            return true;
        };
    }
}
```

---

## 2. Aspect-Oriented Programming (AOP)

AOP lets you add cross-cutting concerns (logging, timing, security checks, retry) to
methods without modifying those methods.

### Concepts

```java
@Aspect     — marks a class as containing advice
@Pointcut   — defines WHICH methods to intercept (the "where")
@Around     — runs before AND after the method (most powerful)
@Before     — runs before the method
@After      — runs after the method (whether it succeeds or fails)
@AfterReturning — runs after the method returns successfully
@AfterThrowing  — runs when the method throws an exception
```

### Execution Timing Aspect (Practical Example)

```java
/**
 * Logs the execution time of any method annotated with {@code @Timed}.
 *
 * <p>Place {@code @Timed} on any service method to automatically log
 * how long it takes. Useful for identifying slow methods in production.</p>
 *
 * <p>Example usage:</p>
 * <pre>
 *   {@literal @}Timed
 *   public OrderResponse createOrder(CreateOrderRequest request) { ... }
 * </pre>
 */
@Aspect
@Component
@Slf4j
public class ExecutionTimingAspect {

    /**
     * Intercepts any method annotated with @Timed in any class.
     */
    @Around("@annotation(com.acme.annotation.Timed)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long startTime    = System.currentTimeMillis();

        try {
            // Proceed runs the actual method
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            log.info("[TIMING] {} completed in {}ms", methodName, duration);
            return result;

        } catch (Exception ex) {
            long duration = System.currentTimeMillis() - startTime;
            log.warn("[TIMING] {} failed after {}ms — {}", methodName, duration, ex.getMessage());
            throw ex;  // re-throw so normal exception handling still works
        }
    }
}
```

### Retry Aspect

```java
/**
 * Automatically retries methods annotated with {@code @Retryable} on transient failures.
 *
 * <p>Uses exponential backoff: first retry after 1s, second after 2s, third after 4s.</p>
 */
@Aspect
@Component
@Slf4j
public class RetryAspect {

    @Around("@annotation(retryable)")
    public Object retryOnFailure(ProceedingJoinPoint joinPoint, Retryable retryable) throws Throwable {
        int maxAttempts    = retryable.maxAttempts();
        long delayMs       = retryable.initialDelayMs();
        String methodName  = joinPoint.getSignature().toShortString();

        Exception lastException = null;

        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                return joinPoint.proceed();

            } catch (Exception ex) {
                lastException = ex;
                log.warn("[RETRY] {} failed on attempt {}/{}: {}",
                         methodName, attempt, maxAttempts, ex.getMessage());

                if (attempt < maxAttempts) {
                    Thread.sleep(delayMs);
                    delayMs *= 2;  // exponential backoff
                }
            }
        }

        log.error("[RETRY] {} exhausted all {} attempts", methodName, maxAttempts);
        throw lastException;
    }
}
```

---

## 3. Spring WebFlux — Reactive Programming

Use WebFlux when you need to handle thousands of concurrent connections with minimal threads.
Key types: `Mono<T>` (0 or 1 item) and `Flux<T>` (0 to N items).

### When to Use WebFlux vs MVC

```json
Spring MVC (servlet, blocking)  → Use when:
  - Team is comfortable with blocking/imperative code
  - Simpler CRUD app, low-to-medium concurrency
  - Using JDBC (blocking by nature)

Spring WebFlux (reactive, non-blocking)  → Use when:
  - High concurrency (thousands of simultaneous requests)
  - Composing multiple async I/O calls (microservice fan-out)
  - Using reactive DB drivers (R2DBC, MongoDB reactive)
  - Streaming responses (SSE, websockets)
```

### Reactive REST Controller

```java
/**
 * Reactive REST controller for order operations.
 *
 * <p>Returns Mono/Flux instead of plain objects. Spring WebFlux subscribes
 * to these and writes the result to the HTTP response when it arrives —
 * without blocking a thread while waiting.</p>
 */
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    /**
     * Returns a single order by ID.
     * Mono = 0 or 1 result. Returns 404 if empty.
     */
    @GetMapping("/{id}")
    public Mono<ResponseEntity<OrderResponse>> getOrder(@PathVariable Long id) {
        return orderService.findById(id)
            .map(order -> ResponseEntity.ok(order))
            .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    /**
     * Returns all orders for a customer as a stream.
     * Flux = 0 to N results. Streams items as they are produced.
     */
    @GetMapping("/stream")
    @Produces(MediaType.TEXT_EVENT_STREAM_VALUE)   // Server-Sent Events
    public Flux<OrderResponse> streamOrders(@RequestParam Long customerId) {
        return orderService.streamOrdersForCustomer(customerId);
    }
}
```

### Composing Multiple Async Calls

```java
/**
 * Fetches a full order summary by combining data from three services in parallel.
 *
 * <p>Uses Mono.zip() to run all three calls concurrently and combine the results.
 * Total time ≈ max(orderTime, customerTime, inventoryTime) instead of sum.</p>
 */
public Mono<OrderSummary> getOrderSummary(Long orderId) {
    Mono<Order>     orderMono     = orderRepository.findById(orderId);
    Mono<Customer>  customerMono  = customerService.findById(orderId);
    Mono<Inventory> inventoryMono = inventoryService.checkAvailability(orderId);

    // Zip runs all three in parallel and combines results when all arrive
    return Mono.zip(orderMono, customerMono, inventoryMono)
               .map(tuple -> OrderSummary.builder()
                   .order(tuple.getT1())
                   .customer(tuple.getT2())
                   .inventory(tuple.getT3())
                   .build());
}
```

### Error Handling in Reactive Chains

```java
return orderService.findById(orderId)
    // Transform NotFound into a 404 response
    .switchIfEmpty(Mono.error(new OrderNotFoundException(orderId)))
    // Catch and map specific exceptions
    .onErrorResume(OrderNotFoundException.class,
                   ex -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage())))
    // Log and re-throw unexpected errors
    .doOnError(ex -> log.error("Unexpected error for order {}: {}", orderId, ex.getMessage()));
```

---

## 4. Spring Batch — Bulk Processing

### Core Concepts

```java
Job        → the whole batch process (has a name, can be restarted)
Step       → one phase of the job (read → process → write, or a tasklet)
ItemReader → reads records one at a time from a source (DB, file, queue)
ItemProcessor → transforms or filters one record (optional)
ItemWriter → writes a chunk of records to a destination
Chunk      → a batch of N records processed in a single transaction
```

### Typical Batch Job

```java
/**
 * Batch job that reads unprocessed orders from the database,
 * calculates totals, and writes back the results.
 *
 * <p>Chunk size of 100 means: read 100 → process 100 → write 100 in one transaction.
 * If any of the 100 fail, only that chunk is rolled back — not the whole job.</p>
 */
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class OrderProcessingJobConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final OrderItemReader orderItemReader;
    private final OrderProcessor   orderProcessor;
    private final OrderItemWriter  orderItemWriter;

    /**
     * Defines the full job with one step.
     */
    @Bean
    public Job processOrdersJob() {
        return new JobBuilder("processOrdersJob", jobRepository)
            .start(processOrdersStep())
            .build();
    }

    /**
     * Defines the chunk-oriented step.
     * Reads 100 orders at a time, processes each, writes in bulk.
     */
    @Bean
    public Step processOrdersStep() {
        return new StepBuilder("processOrdersStep", jobRepository)
            .<Order, ProcessedOrder>chunk(100, transactionManager)
            .reader(orderItemReader)
            .processor(orderProcessor)
            .writer(orderItemWriter)
            .faultTolerant()
            .skipLimit(10)                          // skip up to 10 bad records before failing
            .skip(MalformedOrderException.class)    // skip orders that can't be parsed
            .build();
    }
}
```

---

## 5. Spring Cloud — Microservice Infrastructure

### Circuit Breaker (Resilience4j)

```java
/**
 * Calls the external inventory service with circuit breaker protection.
 *
 * <p>If the inventory service returns errors or times out too many times,
 * the circuit "opens" and we immediately return the fallback without
 * even trying the call — protecting both us and the failing service.</p>
 *
 * @param productId the product to check
 * @return available quantity, or 0 if the service is unavailable
 */
@CircuitBreaker(name = "inventory-service", fallbackMethod = "inventoryFallback")
@TimeLimiter(name = "inventory-service")    // timeout after configured duration
public CompletableFuture<Integer> checkInventory(Long productId) {
    return CompletableFuture.supplyAsync(() ->
        inventoryClient.getAvailableQuantity(productId)
    );
}

/**
 * Fallback used when the circuit is open or the call times out.
 * Returns 0 (assume out of stock) and logs for alerting.
 *
 * @param productId the product that was being checked
 * @param ex        the exception that triggered the fallback
 */
private CompletableFuture<Integer> inventoryFallback(Long productId, Exception ex) {
    log.warn("Inventory service unavailable for product {}. Using fallback (0). Reason: {}",
             productId, ex.getMessage());
    return CompletableFuture.completedFuture(0);
}
```

### application.yml for Resilience4j

```yaml
resilience4j:
  circuitbreaker:
    instances:
      inventory-service:
        sliding-window-size: 10          # evaluate last 10 calls
        failure-rate-threshold: 50       # open circuit if 50%+ fail
        wait-duration-in-open-state: 30s # wait 30s before trying again (HALF_OPEN)
        permitted-number-of-calls-in-half-open-state: 3

  timelimiter:
    instances:
      inventory-service:
        timeout-duration: 2s             # fail fast after 2 seconds
```

---

## 6. Application Events — Decoupled Communication

```java
// ── Event class (just a data carrier) ─────────────────────────────────────
/**
 * Published when an order is created successfully.
 * Listeners can react to this without the OrderService knowing about them.
 */
public record OrderCreatedEvent(Long orderId, Long customerId, BigDecimal totalAmount) {}


// ── Publisher (the service that creates orders) ────────────────────────────
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final ApplicationEventPublisher eventPublisher;

    @Transactional
    public OrderResponse createOrder(CreateOrderRequest request) {
        Order saved = orderRepository.save(buildOrder(request));

        // Published now, inside the transaction; the listener's AFTER_COMMIT
        // phase is what defers delivery until the commit succeeds
        // (and drops it if the transaction rolls back).
        eventPublisher.publishEvent(new OrderCreatedEvent(
            saved.getId(), saved.getCustomerId(), saved.getTotalAmount()
        ));

        return OrderResponse.from(saved);
    }
}


// ── Listener (email service — completely independent of OrderService) ──────
@Component
@Slf4j
public class OrderConfirmationEmailListener {

    /**
     * Sends a confirmation email after an order is created.
     *
     * <p>Uses AFTER_COMMIT to guarantee the order is in the DB before sending
     * the email. If we sent during the transaction and it rolled back,
     * the customer would get an email for an order that doesn't exist.</p>
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    @Async   // run in a separate thread so it doesn't slow down the HTTP response
    public void onOrderCreated(OrderCreatedEvent event) {
        log.info("Sending confirmation email for order {}", event.orderId());
        emailService.sendOrderConfirmation(event.customerId(), event.orderId());
    }
}
```

---

## 7. Debugging Spring Applications

### Bean Wiring Issues

```java
// Problem: "No qualifying bean of type X found"
// Fix: check that the bean is in a @ComponentScan-covered package
// Debug: enable bean definition logging
logging.level.org.springframework.beans=DEBUG

// Problem: Circular dependency
// Spring Boot 2.6+ disallows circular deps by default.
// Fix: extract the shared logic into a third bean, or use @Lazy on one injection point.
@Lazy  // Spring will inject a proxy and only create the bean when first used
private final ServiceB serviceB;
```

### Transaction Debugging

```java
// See every transaction begin/commit/rollback
logging.level.org.springframework.transaction=TRACE
logging.level.org.springframework.orm.jpa=DEBUG

// Common problem: @Transactional doesn't work
// Cause 1: Method is called from within the same class (Spring AOP can't intercept it)
// Fix: Call via the injected Spring bean, not via 'this.methodName()'
// Cause 2: @Transactional on a private method (AOP can't proxy private methods)
// Fix: Make the method protected or public
```

### Startup Performance

```java
// Slow startup? Enable startup timing:
logging.level.org.springframework.boot=DEBUG

// Or add to main class:
SpringApplication app = new SpringApplication(MyApp.class);
app.setLazyInitialization(true);  // beans created on first use, not at startup
```

---

## 8. Key Debugging Quick Reference

| Problem | Likely Cause | Fix |
|---------|-------------|-----|
| `@Transactional` silently ignored | Self-invocation (calling `this.method()`) | Inject `self` reference or refactor to separate class |
| Bean not found at startup | Outside component scan package | Move to correct package or add `@ComponentScan(basePackages=...)` |
| `LazyInitializationException` | Accessing lazy collection outside transaction | Use `@Transactional(readOnly=true)` on the calling method |
| AOP advice not applied | Method is `private` or `final` | Make `protected` or `public`; remove `final` |
| `@Async` not working | Missing `@EnableAsync` | Add `@EnableAsync` to a `@Configuration` class |
| `@Scheduled` not running | Missing `@EnableScheduling` | Add `@EnableScheduling` to a `@Configuration` class |
| Application context fails to load in tests | Bean dependency issue | Use `@MockBean` for external dependencies |

Files in this skill

  • README.md10.2 KB
  • adr_skill.md8.4 KB
  • agent_skill_design_skill.md3.1 KB
  • apache_camel_skill.md15.5 KB
  • apache_pulsar_skill.md17.1 KB
  • ba_create_skill.md18.9 KB
  • backend_skill.md22.1 KB
  • code_documentation_skill.md13.4 KB
  • code_formatting_skill.md11.7 KB
  • code_health_skill.md9.8 KB
  • code_review_skill.md36.7 KB
  • context_builder_skill.md11.7 KB
  • current_tech_spec_skill.md6.3 KB
  • database_skill.md18.4 KB
  • debugging_skill.md3.4 KB
  • error_handling_skill.md18.4 KB
  • frontend_skill.md23.6 KB
  • java_advanced_skill.md15 KB
  • jira_html_report_skill.md15.5 KB
  • jira_incremental_spec_generator_skill.md19.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.