Spring Boot¶
Core container¶
What is IoC and dependency injection? Why use it?
Inversion of control: the framework creates and wires objects instead of your code calling new.
Dependency injection is how: a bean declares what it needs and the container supplies it. Benefits: loose
coupling, easy testing (inject mocks), configuration in one place, cross-cutting features via proxies.
Constructor vs. field vs. setter injection?
Constructor injection is preferred: dependencies are explicit and final, the object is never half-built, and
tests can call the constructor without Spring. With one constructor, @Autowired is optional. Field injection
hides dependencies and needs reflection to test. Setter injection only for optional dependencies.
What are bean scopes?
singleton (default, one per container), prototype (new each time it's requested), and in web apps request,
session, application, websocket. Injecting a prototype into a singleton gives you one instance forever;
use ObjectProvider<T> or a scoped proxy to get a fresh one.
Describe the bean lifecycle.
Instantiate → inject dependencies → *Aware callbacks → BeanPostProcessor.postProcessBeforeInitialization →
@PostConstruct / afterPropertiesSet / init-method → postProcessAfterInitialization (where AOP proxies are
created) → in use → @PreDestroy / destroy on shutdown.
@Component vs. @Service vs. @Repository vs. @Controller? @Bean vs. @Component?
All are @Component stereotypes found by component scanning. @Repository also translates persistence
exceptions into DataAccessException; @Controller/@RestController are web handlers. @Bean on a method in a
@Configuration class registers a bean you construct yourself, typically for third-party classes you can't
annotate.
Two beans of the same type: how does Spring choose?
It fails with NoUniqueBeanDefinitionException unless you use @Primary on one, @Qualifier("name") at the
injection point, or inject a List<T>/Map<String, T> of all of them (handy for strategy patterns).
How do you handle circular dependencies?
Spring Boot 2.6+ forbids them by default. Fix the design: extract the shared part into a third bean, use events,
or inject lazily (@Lazy, ObjectProvider). A circular dependency is usually a sign of mixed responsibilities.
Spring Boot specifically¶
How does auto-configuration work?
@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan. Auto-configuration
classes are listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (older:
spring.factories) and guarded by conditions: @ConditionalOnClass (library on the classpath),
@ConditionalOnMissingBean (you didn't define your own), @ConditionalOnProperty. So adding spring-boot-starter-
data-redis gives you a RedisTemplate unless you define one. Debug with --debug (conditions report) or the
/actuator/conditions endpoint.
What is a starter?
A dependency bundle with sensible versions plus auto-configuration for a feature: spring-boot-starter-web,
-data-jpa, -security, -actuator, -test.
How do profiles and externalised configuration work?
Properties come from many sources with an order of precedence (command-line args > env vars > profile-specific
files > application.yml > defaults). spring.profiles.active=prod loads application-prod.yml. Bind groups of
properties to a typed, validated class with @ConfigurationProperties. Secrets come from env vars or a secret
store (AWS Secrets Manager, Vault), never committed.
What does Actuator give you?
Production endpoints: /actuator/health (liveness/readiness for Kubernetes probes), /metrics, /prometheus
(Micrometer), /info, /env, /loggers (change log levels at runtime), thread dump, heap dump. Secure or limit
them in production.
Prayog link: Micrometer metrics scraped by Prometheus, shown in Grafana from gateway to ledger.
Web and REST¶
What happens when an HTTP request hits a Spring MVC app?
Tomcat thread → filters (security, logging) → DispatcherServlet → HandlerMapping finds the controller method
→ interceptors → argument resolvers (@PathVariable, @RequestBody via Jackson HttpMessageConverter,
validation) → controller → return value converted to JSON → response. Exceptions go to @ExceptionHandler /
@ControllerAdvice.
How do you do global exception handling?
A @RestControllerAdvice with @ExceptionHandler methods that map exceptions to status codes and a consistent
error body (Spring 6 supports RFC 7807 ProblemDetail). Don't leak stack traces; log with a correlation id.
What makes an API RESTful? Which methods are idempotent?
Resources as nouns (/models/{id}), HTTP methods as verbs, proper status codes, stateless requests,
representations (JSON). Idempotent: GET, PUT, DELETE, HEAD, OPTIONS (repeating has the same effect).
Not: POST, PATCH (generally). Make POST safe to retry with an idempotency key.
Prayog link: client order IDs act as idempotency keys: a retried order is rejected as
DUPLICATE_CLIENT_ORDER_ID instead of trading twice.
Status codes you should know?
200 OK, 201 Created, 202 Accepted, 204 No Content; 400 Bad Request, 401 Unauthorized (not authenticated), 403 Forbidden (authenticated, not allowed), 404, 409 Conflict, 422 Unprocessable, 429 Too Many Requests; 500, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.
How do you version an API?
URI (/v1/..., simplest, what Prayog uses: /api/v1/...), header (Accept: application/vnd.x.v2+json), or
query param. Prefer additive, backward-compatible changes so you rarely need a new version.
Spring MVC vs. WebFlux?
MVC: thread per request, blocking, simple to reason about and debug. WebFlux: non-blocking on Netty with
Reactor (Mono/Flux), handles many concurrent connections with few threads, good for streaming and WebSockets,
but the whole call chain must be non-blocking and debugging is harder. With virtual threads (Java 21), MVC handles
high concurrency too, which removes most reasons to go reactive for plain I/O.
Prayog link: the gateway uses WebFlux because responses are completed asynchronously after the journal's fsync, and it serves WebSocket market data.
How do you validate input?
Bean Validation: @Valid @RequestBody OrderRequest req with @NotNull, @Positive, @Size on fields; custom
constraints for business rules; handle MethodArgumentNotValidException in the advice for a 400 with field errors.
Data and transactions¶
How does @Transactional work? Common pitfalls?
Spring wraps the bean in a proxy; the proxy starts a transaction before the method and commits or rolls back after. Pitfalls:
- Self-invocation: calling a
@Transactionalmethod from another method in the same class bypasses the proxy, so no transaction. - Only public methods are proxied (by default).
- By default rollback only on unchecked exceptions; use
rollbackFor = Exception.classfor checked ones. - Catching the exception inside the method means no rollback.
- Long transactions holding locks while calling remote services.
Explain propagation and isolation.
Propagation: REQUIRED (default: join or start), REQUIRES_NEW (suspend outer, start a new one: e.g. audit
log that must commit even if the outer rolls back), NESTED (savepoint), SUPPORTS, MANDATORY, NEVER.
Isolation: READ_UNCOMMITTED (dirty reads), READ_COMMITTED (default in Postgres/Oracle; no dirty reads),
REPEATABLE_READ (default in MySQL; no non-repeatable reads), SERIALIZABLE (no phantoms; slowest).
What is the N+1 problem?
Loading a list of N entities, then lazily loading a relation for each one: 1 + N queries. Fix with a join fetch
(JOIN FETCH, @EntityGraph), batch fetching, or a single query that returns what you need (MyBatis result maps
with joins).
JPA/Hibernate vs. MyBatis vs. jOOQ vs. JDBC template?
JPA: entities and generated SQL; productive for CRUD, risky for complex queries (N+1, surprises). MyBatis: you
write SQL, it maps results (what I used at work). jOOQ: type-safe SQL built in Java from the schema (what Prayog
uses). JdbcTemplate: thin wrapper over JDBC. Pick by how much control over SQL you need.
Security¶
How does Spring Security work at a high level?
A chain of servlet filters (SecurityFilterChain). Authentication filters build an Authentication (from a
session, Basic auth, or a JWT), store it in the SecurityContext; authorisation checks rules on URLs
(requestMatchers(...).hasRole(...)) or methods (@PreAuthorize).
How does JWT/OAuth2 resource-server authentication work?
The client gets an access token from the identity provider (Keycloak, Okta, Cognito) via OAuth2/OIDC. The API validates each token offline: checks the signature against the provider's public keys (JWKS, cached), plus expiry, issuer and audience. No call to the IdP per request. Roles/scopes come from claims. Trade-off: a JWT can't be revoked before it expires, so keep access tokens short-lived.
Prayog link: gateway checks JWTs offline against Keycloak's JWKS; the rate-limit tier comes from the token.
What is CSRF and when do you need protection? What is CORS?
CSRF: a malicious site makes the browser send a request with your cookies. Needed for cookie-based sessions;
not for APIs authenticated with a bearer token in a header. CORS: the browser blocks cross-origin requests
unless the server allows the origin via Access-Control-Allow-Origin (and answers the preflight OPTIONS).
Testing¶
How do you test a Spring Boot app?
- Unit tests: plain JUnit 5 + Mockito, no Spring context (fast).
- Slices:
@WebMvcTest(controller + MockMvc),@DataJpaTest,@JsonTest. - Integration:
@SpringBootTestwith Testcontainers for a real Postgres/Kafka/Redis. - Contract tests for APIs other teams call.
Prayog link: Testcontainers for Postgres and Kafka in the post-trade ITs, a paused Kafka broker in
KafkaPublisherIT.
Microservices patterns¶
How do services communicate? Sync vs. async?
Sync (REST/gRPC): simple, immediate answer, but couples availability (if B is down, A fails). Async (Kafka, SQS): decouples in time, absorbs load, but eventual consistency and harder debugging. Use sync for queries the user waits on, async for events and work that can happen later.
Circuit breaker, retry, bulkhead, timeout?
Timeout: never wait forever. Retry with exponential backoff and jitter, only for idempotent operations. Circuit breaker: after N failures, fail fast for a while, then try again (half-open). Bulkhead: separate thread pools/limits per dependency so one slow dependency can't exhaust everything. Resilience4j provides all.
How do you keep data consistent across services (distributed transactions)?
Avoid two-phase commit. Use the Saga pattern (a sequence of local transactions with compensating actions, orchestrated or choreographed) and the transactional outbox (write the event to an outbox table in the same DB transaction as the change; a relay publishes it to Kafka) so the DB and the event never disagree. Consumers must be idempotent.
Prayog link: the Kafka publisher tails the journal, which is the same idea as an outbox: the durable record comes first, publishing follows from it.