Spring Boot Interview Series: 10 Core Questions (Part 2) – Why @Configuration Uses CGLIB Proxy
This article answers ten Spring Boot interview questions, covering why @Configuration classes are CGLIB‑proxied, common startup‑time bottlenecks and diagnostics, graceful shutdown mechanics, execution timing of ApplicationContextInitializer vs ApplicationRunner, circular‑dependency handling, health‑endpoint aggregation, distributed‑transaction strategies, Kubernetes probes, JSON serialization safeguards, and migration challenges to Jakarta EE 9+.
1. CGLIB proxy for @Configuration classes and @Bean method interception
Spring Boot registers a CGLIB proxy for every @Configuration class during the parsing phase via ConfigurationClassPostProcessor, even without @EnableAspectJAutoProxy. The proxy ensures that calls to @Bean methods always return the singleton instance stored in the container.
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() { return new ServiceA(); }
@Bean
public ServiceB serviceB() { return new ServiceB(serviceA()); }
}Without a proxy, serviceB() invoking serviceA() creates a new ServiceA instance, breaking the singleton guarantee.
With the CGLIB proxy, the call to serviceA() is intercepted and the already‑registered singleton bean is retrieved from the Spring container.
Key point: proxy guarantees singleton semantics of @Bean methods.
2. Typical reasons for slow Spring Boot startup and systematic diagnosis/optimization
Common causes
Excessive auto‑configuration classes – many @ConditionalOn… checks consume time.
Overly broad @ComponentScan ranges – scanning unnecessary packages.
Slow database‑connection initialization – e.g., HikariCP timeouts or DNS delays.
Third‑party SDK initialization (Redis, Kafka, etc.).
Class‑loading overhead – many JARs or large fat‑JAR extraction.
Diagnostic tools
Enable StartupInfoLogger with
logging.level.org.springframework.boot.StartupInfoLogger=DEBUG.
Use Spring Boot 2.7+ Startup Metrics and inspect /actuator/metrics/startup.*.
Profile with Async Profiler or Java Flight Recorder (JFR) for CPU/lock/IO analysis.
Optimization measures
Exclude unnecessary auto‑configuration, e.g.
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}).
Restrict @ComponentScan to required packages.
Enable lazy initialization (Spring Boot 2.2+): spring.main.lazy-initialization=true.
Use GraalVM native image (Spring Boot 3+) for millisecond‑level startup.
Key point: performance engineering, observability, startup‑optimization strategies.
3. Graceful shutdown mechanism and pitfalls
Enable graceful shutdown (Spring Boot 2.3+):
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30sInternal steps after receiving SIGTERM
Stop accepting new requests (Tomcat/Jetty reject new connections).
Wait for in‑flight requests to finish (up to timeout-per-shutdown-phase).
Close resources in order: web container → DisposableBean.destroy() → ApplicationContext shutdown.
The sequence is coordinated via Spring's Lifecycle and SmartLifecycle interfaces.
Common pitfalls
Long‑running tasks (e.g., while(true)) are not interrupted; they must listen to ContextClosedEvent.
Asynchronous @Async tasks may be terminated abruptly – use a TaskExecutor and call awaitTermination to finish them.
In Kubernetes, mark the pod NotReady via readinessProbe before shutdown to avoid new traffic.
Key point: cloud‑native lifecycle management.
4. Execution timing and use‑cases of ApplicationContextInitializer vs ApplicationRunner/CommandLineRunner
ApplicationContextInitializerExecuted before refresh() – the context is created but bean definitions are not yet loaded.
Typical use: programmatically modify Environment, add PropertySource, decrypt configuration (e.g., Vault injection, Kubernetes ConfigMap binding). ApplicationRunner / CommandLineRunner Executed after refresh() – all beans are fully initialized.
Typical use: run business initialization logic such as data warm‑up, cache loading, DB migration, or start scheduled jobs.
Key point: extension‑point timing and layering of initialization logic.
5. Circular‑dependency handling and three‑level cache
Spring Boot does not modify Spring Framework's circular‑dependency resolution; it still relies on the three‑level cache: singletonObjects – final singleton pool. earlySingletonObjects – early‑exposed beans (properties not yet injected). singletonFactories – ObjectFactory used to create early references.
Notes:
Constructor injection with @Autowired cannot resolve circular dependencies and will fail.
Field or setter injection can be resolved by the three‑level cache but is considered a workaround.
Refactor code (introduce mediator, event‑driven design) to eliminate circular dependencies rather than relying on the framework.
Key point: IoC container principles and anti‑pattern detection.
6. Aggregation of multiple HealthIndicators in /health and custom "partial‑failure‑UP" logic
Default behavior: if any HealthIndicator returns DOWN, the overall status is DOWN.
Custom aggregation can be implemented by providing a HealthAggregator (or ManagementHealthAggregator) bean:
@Component
public class CustomHealthAggregator implements HealthAggregator {
@Override
public Health aggregate(Map<String, Health> healths) {
Status status = Status.UP;
// Example: only mark DOWN when a core service is down
if (isCoreServiceDown(healths)) {
status = Status.DOWN;
}
return new Health.Builder(status).withDetails(healths).build();
}
private boolean isCoreServiceDown(Map<String, Health> healths) {
return healths.get("database") != null &&
healths.get("database").getStatus() == Status.DOWN;
}
}Configure the custom aggregator:
management:
endpoint:
health:
show-details: always
health:
defaults:
aggregator: customHealthAggregatorKey point: observability customization and business‑oriented health modeling.
7. Distributed transaction handling in Spring Boot – Seata vs local‑transaction + Saga
Seata (AT mode)
Applicable scenario: strong consistency, cross‑DB transactions.
Advantages: low code intrusion, supports rollback.
Disadvantages: high performance cost, requires undo_log table.
Local transaction + Saga
Applicable scenario: eventual consistency, long‑running processes.
Advantages: high performance, no global lock.
Disadvantages: compensation logic must be written, higher complexity.
Message queue + local‑transaction table
Applicable scenario: asynchronous decoupling, high throughput.
Advantages: reliable, easy to scale.
Disadvantages: introduces latency, needs duplicate‑consume handling.
Seata integration steps
Add dependency seata-spring-boot-starter.
Configure application.yml to point to the Seata server.
Annotate business methods with @GlobalTransactional.
Selection guidance
Financial core systems → Seata AT / TCC.
E‑commerce order flow → Saga (order → inventory → points).
Logging/notification services → Message‑queue eventual consistency.
Key point: distributed system design trade‑offs.
8. Designing liveness and readiness probes for Spring Boot on Kubernetes
Liveness probe (detects if the app is alive; failure triggers pod restart):
Endpoint: /actuator/health/liveness Sample configuration:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 3Do not include external dependencies (e.g., DB connections) in the liveness check to avoid false restarts.
Readiness probe (determines if the app is ready to receive traffic; failure removes the pod from service endpoints):
Endpoint: /actuator/health/readiness Sample configuration:
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 1Readiness checks may include critical dependencies such as DB or Redis.
Spring Boot 2.3+ automatically registers livenessState and readinessState health indicators, visible via /actuator/health.
Key point: cloud‑native health probing and elastic scaling.
9. Preventing infinite recursion and sensitive‑data leakage in JSON serialization
Problem scenarios
Bidirectional entity relationships (e.g., User ↔ Order) cause Jackson stack overflow.
Sensitive fields (password, ID number) are unintentionally returned.
Solutions
Use @JsonManagedReference / @JsonBackReference to break recursion.
Apply @JsonIgnore for quick exclusion.
Map entities to DTOs and return DTOs instead of entities.
Mark sensitive fields with @JsonIgnore.
Control field visibility per endpoint with @JsonView:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String password;
}
@JsonView(Views.Public.class)
@GetMapping("/user")
public User getUser() { ... }Global Jackson configuration example:
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.failOnEmptyBeans(false)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}Key point: secure API design and serialization control.
10. Compatibility issues when migrating to Spring Boot 3 / Jakarta EE 9+ and upgrade strategy
Major changes
All javax.* packages are renamed to jakarta.* (e.g., javax.servlet → jakarta.servlet, javax.persistence → jakarta.persistence, javax.validation → jakarta.validation).
Compatibility problems
Third‑party libraries that still depend on javax.* (e.g., older MyBatis, Hibernate).
Custom filters or listeners need their import statements updated.
Swagger/OpenAPI libraries must be upgraded to versions that support Jakarta (e.g., Springdoc OpenAPI 1.6+).
Step‑by‑step upgrade strategy
Upgrade to Spring Boot 2.7 first and fix all warnings.
Upgrade to Spring Boot 3.x with JDK 17+.
Batch replace javax. imports with jakarta. (IDE refactor or script).
Verify that every dependency is compatible with Jakarta (inspect Maven/Gradle dependency tree).
Key point: technical debt management and major‑version migration.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer1970
Formerly called 'Code to 35'. Add our main WeChat ID to access a wealth of shared resources (algorithms, interview prep, tech stacks: Java, Python, Go, big data). We mainly share serious development techniques, focusing on output-driven input. Occasionally we post life snippets and gossip. Our aim is to attract precise traffic and test advertising opportunities.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
