Skip to content
Web and Mobile Development

Spring interview questions and answers

46 questions, 9 with code. About 3 h 36 min to read them all.

By TestLab · Updated

Plan for an interview
46 questions

Track what you know and what to revise.

Sign in to track progress

You can open a few full answers without signing in. Sign in to read all

1

What are the steps to build a basic Spring application from scratch?

A simple Spring application is created with Spring Boot: you generate a project skeleton (usually from Spring Initializr), add a starter dependency such as spring-boot-starter-web, write a class annotated with @SpringBootApplication containing a main method, and run it — auto-configuration wires up an embedded server so no external container or XML is needed.

Read full answer

Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17 or later

How It Works

  • Starters: spring-boot-starter-web pulls in Spring MVC, Jackson and an embedded Tomcat, so the app is runnable as a plain main method.

  • Auto-configuration: @SpringBootApplication combines @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan. Boot inspects the classpath and configures beans (DispatcherServlet, JSON converters, DataSource, and so on) that you have not declared yourself.

  • Component scanning: classes annotated with @Component, @Service, @Repository or @Controller in the main class’s package and below are detected and registered. Put the main class in the root package, above everything else.

  • Embedded server: SpringApplication.run starts Tomcat on port 8080 by default, without deploying a WAR file.

  • Jakarta namespace: Boot 3 uses jakarta.* packages (jakarta.servlet, jakarta.persistence). Code importing javax.* is Boot 2 and will not work here.

  • Configuration: external settings live in application.properties or application.yaml under src/main/resources.

Steps to Create the Project

  1. Open Spring Initializr at start.spring.io (or the equivalent wizard built into IntelliJ IDEA, VS Code or Eclipse).

  2. Choose the build tool (Maven or Gradle), Java 17 or later, and the current Spring Boot 3.x release.

  3. Add the Spring Web dependency; optionally add Spring Boot DevTools, Validation or Actuator.

  4. Generate and unzip the archive, then open it in your IDE.

  5. Run ./mvnw spring-boot:run (on Windows, mvnw.cmd spring-boot:run) or start the main method from the IDE. For Gradle use ./gradlew bootRun.

  6. Verify with curl http://localhost:8080/greet?name=Ada.

  7. Package with ./mvnw clean package and run the executable jar: java -jar target/demo-0.0.1-SNAPSHOT.jar. Tests run with ./mvnw test.

Anatomy of the Generated Project

Path Purpose
pom.xml / build.gradle Inherits spring-boot-starter-parent (Maven) or the Spring Boot Gradle plugin; declares starters
src/main/java/.../DemoApplication.java Entry point with @SpringBootApplication
src/main/resources/application.properties spring.application.name and other settings
src/test/java/.../DemoApplicationTests.java Context-loads test using @SpringBootTest
mvnw, mvnw.cmd, .mvn/ Maven Wrapper so no local Maven install is required

Code Example

// src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Combines @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args); // starts embedded Tomcat
    }
}
// src/main/java/com/example/demo/GreetingService.java
package com.example.demo;

import org.springframework.stereotype.Service;

@Service // detected by component scan
public class GreetingService {

    public String greet(String name) {
        return "Hello, " + name + "!";
    }
}
// src/main/java/com/example/demo/GreetingController.java
package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private final GreetingService greetingService;

    // Constructor injection; @Autowired is not needed for a single constructor
    public GreetingController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    @GetMapping("/greet")
    public String greet(@RequestParam(defaultValue = "World") String name) {
        return greetingService.greet(name); // GET /greet?name=Ada
    }
}

Best Practices and Common Pitfalls

  • Prefer constructor injection over field injection with @Autowired: dependencies become explicit, and the field can be final.

  • Do not add @ComponentScan or @EnableAutoConfiguration manually next to @SpringBootApplication; it is redundant and can duplicate scanning.

  • Keep the main class in the top-level package. Moving it into a subpackage silently stops controllers and services from being found.

  • Do not fight auto-configuration with XML. Use application.yaml/application.properties for settings and @Configuration classes with @Bean methods only when you must replace a default.

  • Common pitfalls: a missing starter (for example no spring-boot-starter-web, so the app exits immediately), a javax.* import left over from a Boot 2 tutorial, and port 8080 already in use — change it with server.port.

  • Where to go next: add spring-boot-starter-data-jpa and spring-boot-starter-validation for persistence and request validation, and spring-boot-starter-actuator for health checks and metrics.

2

Describe the role and purpose of the ApplicationContext interface in the Spring Framework.

The ApplicationContext is the central interface of the Spring Framework’s IoC container: it creates, wires and manages the lifecycle of beans, and adds enterprise services such as event publishing, internationalization and resource loading on top of the basic BeanFactory contract.

Read full answer

Applies toSpring Framework 6.x with Spring Boot 3.x (Java 17+)

How It Works

  • A concrete context implementation reads configuration metadata — Java @Configuration classes, XML files, or the Groovy bean-definition DSL — and uses it to build bean definitions.

  • The container instantiates beans, injects their dependencies, applies BeanPostProcessors, and runs destruction callbacks such as @PreDestroy methods when the context is closed.

  • ApplicationContext extends ListableBeanFactory and HierarchicalBeanFactory (both of which extend BeanFactory), and also extends ApplicationEventPublisher, MessageSource, ResourcePatternResolver and EnvironmentCapable.

  • Unlike a plain BeanFactory, which instantiates singletons lazily, an ApplicationContext pre-instantiates singleton beans at startup and automatically registers BeanPostProcessor and BeanFactoryPostProcessor beans, so wiring errors surface early.

  • It resolves bean scopes — singleton (the default), prototype and the web scopes request, session and application — and lets code look beans up by name or type with getBean(...).

Key Features

  • Dependency Injection: wires collaborating objects and manages their lifecycle; constructor injection is the recommended style.

  • Event publishing: the ApplicationEventPublisher contract lets beans publish events that others consume with @EventListener.

  • Resource loading: the ResourceLoader/ResourcePatternResolver contract loads files, URLs and classpath resources.

  • Internationalization: MessageSource resolves localized messages for i18n.

  • AOP integration: beans are proxied so aspects declared with @Aspect are applied transparently.

  • Environment abstraction: Environment exposes profiles and property sources used during configuration.

Common Implementations

Implementation Use case
AnnotationConfigApplicationContext Standalone applications using Java @Configuration classes
ClassPathXmlApplicationContext XML-based configuration; legacy — prefer Java configuration with AnnotationConfigApplicationContext
GenericWebApplicationContext Web applications in a plain Spring Framework deployment
AnnotationConfigServletWebServerApplicationContext Spring Boot 3 servlet web applications; created automatically

WebApplicationContext is the web-aware sub-interface (it exposes getServletContext()); the concrete classes above implement it.

Best Practices

  • Choose the right context type: AnnotationConfigApplicationContext for standalone apps, and let Spring Boot create its own context through SpringApplication.run(...) in Boot apps.

  • Keep beans stateless where possible: the default singleton scope means one shared instance per container, so mutable fields are a common source of bugs.

  • Avoid @Autowired field injection; declare dependencies as final constructor parameters instead.

  • Configure scanning deliberately: in Spring Boot place the application class in a root package so its built-in component scan finds everything, adding an explicit @ComponentScan only for beans outside that package.

  • Close standalone contexts with ConfigurableApplicationContext.close() so destruction callbacks and singletons shut down cleanly.

3

Which bean scopes are available in Spring, and what does each scope control?

A bean scope determines how many instances of a Spring-managed bean exist, who shares them, and how long they live. Spring defines two general scopes, singleton and prototype, plus four web-aware scopes: request, session, application, and websocket.

Read full answer

Applies toSpring Framework 6.2 and Spring Boot 3.5 on Java 17+

Standard Bean Scopes

Scope Instance Behavior
Singleton One instance per Spring IoC container. This is the default scope.
Prototype A new instance is created each time the bean is requested from the container.
Request One instance per HTTP request, in a web-aware ApplicationContext.
Session One instance per HTTP session.
Application One instance per ServletContext (per web application), shared by every ApplicationContext in that application.
WebSocket One instance per WebSocket session.

Scopes are identified by strings: singleton, prototype, request, session, application, and websocket. You select one with the @Scope annotation (org.springframework.context.annotation.Scope), either by name or with the constants ConfigurableBeanFactory.SCOPE_SINGLETON and ConfigurableBeanFactory.SCOPE_PROTOTYPE (org.springframework.beans.factory.config) or WebApplicationContext.SCOPE_REQUEST, SCOPE_SESSION, and SCOPE_APPLICATION (org.springframework.web.context). The convenience annotations @RequestScope, @SessionScope, and @ApplicationScope (package org.springframework.web.context.annotation) combine @Scope with a scoped proxy.

How Each Scope Works

  • Singleton: The container creates the bean once and hands the same instance to every requester. In a typical Spring Boot application that means one application-wide instance. Because it can be accessed concurrently, it should generally be stateless or thread-safe.

  • Prototype: Every retrieval from the container produces a new object. The container instantiates, configures, and assembles it but keeps no further record: initialization callbacks still run, configured destruction callbacks are never called, and the client is responsible for cleanup.

  • Request: The instance lives only for the processing of one HTTP request. Injecting a request-scoped bean into a singleton requires a scoped proxy or another lazy-resolution mechanism.

  • Session: The bean is bound to one client’s HTTP session and can retain state for that session, for example a shopping cart or a partially completed wizard.

  • Application: The instance lives as long as the web application (ServletContext), is shared across all ApplicationContexts of that application, and is exposed as a ServletContext attribute. This differs from singleton, which is one instance per ApplicationContext.

  • WebSocket: The instance lives for the duration of one WebSocket session, not one message. This scope comes from Spring’s WebSocket support and is available only in a WebSocket-aware application.

Dependency and Proxy Considerations

A prototype injected directly into a singleton is constructed once and then reused by that singleton, because ordinary dependency injection resolves the dependency only at injection time. To get a fresh prototype for each operation, inject ObjectProvider<T> or ObjectFactory<T> instead of the prototype itself.

The same issue applies to request-, session-, application-, and websocket-scoped beans injected into long-lived beans. Spring can create a scoped proxy (for example @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)) that resolves the real target lazily against the current request, session, servlet context, or WebSocket session.

Choosing a Scope

  • Use singleton for stateless services, repositories, configuration objects, and other application-wide components.

  • Use prototype when every consumer needs a distinct instance and shared state is not appropriate.

  • Use request or session only when state is naturally tied to one request or one user session.

  • Use application when a bean must span multiple servlet contexts within the same web application.

  • Use websocket for state associated with a single WebSocket connection.

  • Prefer constructor injection for dependencies at every scope, and remember that shorter-lived beans injected into longer-lived ones need a scoped proxy or a provider such as ObjectProvider.

4

What are the different approaches to defining a bean in Spring?

A Spring bean is configured by declaring it in a configuration class or by marking a class with a stereotype annotation so component scanning can detect it.

Read full answer

Applies toSpring Boot 3.3 with Spring Framework 6

How Beans Are Defined

Spring manages objects called beans through a container. You define a bean by either declaring it explicitly in a configuration class or by letting Spring discover it through component scanning. The container then handles instantiation, wiring, and lifecycle management.

Configuration Approaches

Spring offers several ways to define a bean; the first two cover almost all code in a modern Spring Boot application:

  • @Configuration with @Bean: A class annotated with @Configuration contains @Bean factory methods. Each method’s return value is registered as a bean, named after the method by default. Use it when you need programmatic control over construction, typically for third-party types you cannot annotate.

  • Component scanning: Classes annotated with @Component, @Service, @Repository, or @Controller are automatically detected when component scanning is enabled; the bean name is the decapitalized class name by default. @SpringBootApplication already includes @ComponentScan for the main class’s package and its subpackages.

  • @Import: A configuration class can import another configuration class, or an ImportSelector / ImportBeanDefinitionRegistrar implementation, which adds bean definitions programmatically.

  • XML configuration (legacy): <bean> elements in an XML file were the original mechanism and still work, but they are legacy in modern Spring Boot; Java configuration and stereotype annotations replace them.

  • Direct registration: In non-Boot or test code, GenericApplicationContext.registerBean(...) adds a bean without any configuration class.

Approach Use Case
@Configuration + @Bean When you need programmatic control over bean creation, such as configuring third-party classes
Component scanning For application-specific classes where Spring can manage instantiation

Code Example

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

// Explicit @Bean: built programmatically, so it cannot be component-scanned
@Configuration
public class AppConfig {

    @Bean
    public RestClient inventoryClient() {
        return RestClient.builder()
                .baseUrl("https://inventory.example.com")
                .build();
    }
}

// Component-scanned bean: Spring constructs it and injects the bean declared above
@Service
public class InventoryService {

    private final RestClient inventoryClient;

    // Constructor injection; a single constructor needs no @Autowired
    public InventoryService(RestClient inventoryClient) {
        this.inventoryClient = inventoryClient;
    }
}

Best Practices

  • Prefer constructor injection for required dependencies to ensure immutability and testability.

  • Use @Configuration classes for cross-cutting concerns and third-party integrations.

  • Enable component scanning with @SpringBootApplication or @ComponentScan on a configuration class.

  • Avoid field injection with @Autowired as it is less explicit and harder to test.

  • Keep bean definitions focused: one bean per logical component, and never define the same bean twice (a @Bean method and a scanned class with the same name abort startup).

5

What responsibilities does the Spring Core container have in managing objects and their dependencies?

The Spring Core container provides dependency injection (DI) and Inversion of Control (IoC) by creating, configuring, wiring, and managing application objects called beans. It centralizes their lifecycle and allows business code to depend on interfaces rather than concrete implementations.

Read full answer

Applies toSpring Framework 7.0.x and Spring Boot 4.0.x on Java 17+

Core Responsibility

At runtime, the container reads bean definitions, instantiates the required objects, resolves their dependencies, and exposes them through ApplicationContext. This removes the need for application code to manually construct dependency graphs.

For example, a service can receive its repository through its constructor without using new or looking up the repository itself. The container finds the repository bean and supplies it when constructing the service.

Core Interfaces

  • BeanFactory: The fundamental bean container interface. It provides basic operations such as obtaining beans, checking whether a bean exists, and querying bean types.

  • ApplicationContext: The primary application-level interface used by Spring applications. It extends BeanFactory and adds features such as:

  • Internationalization through MessageSource

  • Event publication through ApplicationEventPublisher

  • Access to the application Environment

  • Automatic detection of BeanPostProcessor and other infrastructure beans

  • Integration with Spring-aware scopes such as request and session scopes

  • Bean definitions: Metadata describing how to create and configure each bean, including its class, dependencies, scope, lifecycle, and other properties.

Dependency Injection

The container supports three common injection styles:

  • Constructor injection: Dependencies are supplied when the bean is created. This is the recommended approach because the bean cannot exist in an incomplete state, its dependencies are explicit, and it is straightforward to unit test.

  • Setter injection: Dependencies are supplied after construction through setters. It can be appropriate when a dependency is optional or may be changed.

  • Field injection: Dependencies are injected into fields, often with @Autowired. Although supported, it hides dependencies and makes isolated testing more difficult, so constructor injection is preferable.

If a class has a single constructor, Spring can use it without requiring @Autowired. If a class has multiple constructors, annotate the constructor Spring should use with @Autowired (or otherwise mark the preferred one) so the container can choose it.

Bean Lifecycle and Scopes

The container manages the complete lifecycle of each bean:

  1. It loads the bean definition.

  2. It instantiates the bean.

  3. It resolves and injects dependencies.

  4. It invokes initialization callbacks, such as @PostConstruct.

  5. It applies BeanPostProcessor callbacks before and after the initialization callbacks.

  6. It invokes destruction callbacks, such as @PreDestroy, when the container closes for singleton beans.

The default bean scope is singleton, meaning one instance exists per container. Other scopes include:

  • Prototype: A new instance is created each time the bean is requested.

  • Request: One instance is maintained per HTTP request in a web-aware context.

  • Session: One instance is maintained per HTTP session.

  • Application: One instance is shared across the ServletContext in a web-aware context.

@Lazy can defer creation until a bean is first requested.

Configuration and Component Discovery

In Spring Boot, @SpringBootApplication enables configuration, auto-configuration, and component scanning from the application’s package hierarchy. Classes annotated with stereotypes such as @Component, @Service, and @Repository are discovered automatically.

Java-based configuration uses @Configuration and @Bean methods. This is useful when third-party classes cannot be annotated or when beans require customized construction.

The container also processes infrastructure beans such as BeanPostProcessor, enabling framework features such as AOP proxies. However, features such as transactions, security, and web request handling are implemented by their respective Spring modules and integrated with the container rather than being the container itself.

Best Practices and Common Pitfalls

  • Prefer constructor injection for required dependencies.

  • Keep beans stateless when possible, especially singleton-scoped beans.

  • Avoid circular bean dependencies; Spring Boot’s default configuration rejects them and usually reports a startup error requiring the dependency design to be changed.

  • Do not manually instantiate container-managed beans when the container should control their lifecycle.

  • Avoid overriding bean definitions ambiguously; Spring Boot disables bean-definition overriding by default.

  • Use prototype-scoped beans sparingly because each lookup creates another instance and associated resources.

6

Could you explain what Aspect-Oriented Programming (AOP) means?

Aspect-Oriented Programming (AOP) is a programming paradigm that lets you extract behaviour that is scattered across many classes — logging, transactions, security checks, caching, metrics — into reusable modules called aspects, which are then applied automatically at well-defined points in the program’s execution rather than being written by hand in every method.

Read full answer

Applies toSpring Framework 6.x and Spring Boot 3.x on Java 17+ (these use `jakarta.*` packages; `javax.*` applies only to older versions)

Why AOP Exists

Some concerns cut across the whole application and do not belong to any single business object. In a classical layered service, a single placeOrder method might need transaction handling, audit logging, a metrics counter, and a permission check. Writing those four lines in every service method duplicates code, mixes responsibilities, and guarantees that someone eventually forgets one.

AOP solves this by separating cross-cutting concerns from core business logic. The business method stays focused on the business rule; the aspect declares when and where its extra behaviour should run.

Core Terminology

  • Aspect: the module that packages a cross-cutting concern, in Spring a class annotated with @Aspect.

  • Join point: a point in the program’s execution where an aspect can intervene — in Spring AOP, a method execution.

  • Pointcut: an expression that selects which join points to intercept, for example execution(* com.example.shop.service..(..)) or a pointcut referenced by name from a @Pointcut method.

  • Advice: the action taken at a matched join point (before, after, around, and so on).

  • Target object: the object whose method is being advised.

  • Weaving: the process of linking aspects to the target code. Spring performs this at runtime through a proxy.

  • Proxy: the wrapper object the container hands out instead of the raw bean.

How Spring AOP Works

Spring AOP is proxy-based, not a full bytecode-weaving implementation. When a bean matches a pointcut, the container wraps it in a proxy; calls that arrive through the proxy are intercepted, advice runs, and the call is then forwarded to the target. This is why the Spring AOP variant only supports method-execution join points, whereas full AspectJ can also advise field access, constructors and static initialisation using compile-time or load-time weaving.

Two proxy strategies exist:

  • JDK dynamic proxies: used when the target implements interfaces; only interface methods are advised.

  • CGLIB proxies: used when the target has no interface, or when proxy-target-class is enabled. Spring Boot enables class-based proxies by default, so interfaces are not required.

Advice ordering between multiple aspects is controlled with @Order or by implementing Ordered.

Advice Types

Advice Annotation When it runs
Before @Before Before the method, cannot stop it
After returning @AfterReturning After normal completion; can read the return value
After throwing @AfterThrowing After an exception propagates out
After (finally) @After After either outcome
Around @Around Wraps the call; can modify arguments, return values, or skip the target entirely with ProceedingJoinPoint

Advantages and Limitations

  • Advantages: removes duplication, keeps business classes free of infrastructure code, centralises policy so a change is made in one place, and makes concerns like transactions or security declarative.

  • Limitations: proxy-based interception does not apply to self-invocation (one method of a bean calling another on this), because that call never passes through the proxy; advised methods must be public and non-final for CGLIB; final classes cannot be proxied; and heavy use of pointcut expressions makes control flow harder to follow at a glance.

Best Practices and Common Pitfalls

  • Keep aspects narrow and name them after the concern (AuditAspect, RetryAspect) rather than creating one catch-all aspect.

  • Prefer the most specific advice type; use @Around only when you genuinely need to change arguments or the return value.

  • Inject dependencies with constructor injection in aspects like in any other bean, and keep advice logic small — a heavy aspect slows every advised call.

  • Avoid advising controllers and internal helper classes; target service-layer boundaries.

  • Remember that method visibility matters: private and protected methods are not intercepted by Spring proxies.

  • Do not rely on self-invocation being advised — either call through an injected reference to the bean or move the concern into the caller.

  • If you truly need to advise constructors or non-public methods, use AspectJ weaving instead of Spring AOP, and accept the extra build complexity.

7

What mechanisms does Spring use to provide AOP functionality, and how are aspects applied to Spring beans?

Spring AOP provides cross-cutting behavior through proxy-based interception of eligible Spring bean method calls. Spring detects aspects, creates proxies for matching beans, and executes advice such as @Before, @After, and @Around when the proxied methods are invoked.

Read full answer

Applies toSpring Boot 3.5.x, Spring Framework 6.2.x, and Java 17+

Core Mechanism

Spring’s AOP support works through a proxy around the target bean:

  1. Add the spring-boot-starter-aop dependency to the Maven or Gradle project.

  2. Spring Boot’s AOP auto-configuration enables the required proxy infrastructure.

  3. The auto-proxy creator examines candidate beans and looks for applicable advisors, including aspects annotated with @Aspect.

  4. If an advisor matches a bean method, Spring wraps the bean in a proxy.

  5. Calls made through that proxy invoke the matching advice and may delegate to the original method using ProceedingJoinPoint.proceed().

Spring AOP supports method-execution join points. It is not a full AspectJ implementation, so it does not provide AspectJ’s field-access, constructor-execution, or static-join-point weaving.

Spring also uses this infrastructure for features such as @Transactional, @Async, and @Cacheable.

Advice and Pointcuts

An aspect combines pointcuts with one or more advice methods:

Advice When it executes
@Before Before the target method
@After After the target method, whether it returns or throws
@AfterReturning Only after a normal return
@AfterThrowing Only after an exception
@Around Before and after the target method, with control over whether and when it executes

Pointcuts identify matching methods using expressions such as:

  • execution(* com.example.demo.PaymentService.charge(..))

  • within(com.example.demo.PaymentService.*)

  • @annotation(com.example.demo.Audited)

For non-Boot applications or manual configuration, @EnableAspectJAutoProxy can explicitly activate the infrastructure.

Proxy Types and Scope

Spring supports two proxy strategies:

Proxy strategy Behavior Main limitation
JDK dynamic proxy Implements the bean’s interfaces and delegates to the target Only interface methods can be advised
CGLIB class-based proxy Creates a subclass of the target class The class and advised methods cannot be final

Spring Boot uses class-based proxies by default through spring.aop.proxy-target-class=true. Class-based proxying is convenient for beans without interfaces, but public methods remain the simplest and most portable interception targets.

The target must be a Spring-managed bean, and the call must pass through its proxy. Internal self-invocation—for example, this.charge() from another method of the same PaymentService instance—does not pass through the proxy and therefore bypasses the advice.

Best Practices and Limitations

  • Use constructor injection for dependencies in aspects and target services.

  • Invoke advised methods through another injected Spring bean rather than through self-invocation.

  • Avoid proxying final classes or final methods when using CGLIB.

  • Use @Order when multiple aspects affect the same method and their execution order matters.

  • Keep pointcuts specific to avoid accidentally proxying large portions of the application.

  • Prefer Spring AOP for ordinary service-method interception; use full AspectJ when field or constructor join points are required.

  • Expose production endpoints over HTTPS; AOP interception does not itself provide transport security.

8

In Spring AOP, what is a join point, and which kinds of program executions qualify as one?

A join point in Spring AOP is a point in a program’s execution where an aspect’s advice can be applied. Spring AOP supports method-execution join points, meaning the invocation or execution of a method exposed through a Spring-managed proxy.

Read full answer

Applies toSpring Framework 6.2 and Spring Boot 3.5 on Java 17+

How It Works

When Spring creates an AOP proxy, it identifies eligible method executions as join points. An aspect can select some of those join points with a pointcut, and Spring then applies the associated advice when a matching method is invoked.

For example, if an aspect applies logging advice to all service methods, each intercepted service-method execution is a join point.

Relationship to Other AOP Concepts

Term Meaning
Join point A point in program execution where advice may be applied
Pointcut A selection expression that matches particular join points
Advice The action performed when a matched join point is reached
Aspect The combination of a pointcut and the advice it applies

A typical AOP flow is:

  1. A method execution occurs.

  2. Spring determines whether it is an exposed join point.

  3. The aspect’s pointcut is evaluated.

  4. Matching advice is executed before, after, or around the method.

Supported Join-Point Types

  • Spring AOP: Supports method-execution join points exposed by Spring AOP proxies.

  • AspectJ: Supports a broader set of join points, including method calls, constructor execution, field access, and exception handling.

Spring AOP does not directly expose every possible Java execution point. Its proxy-based implementation primarily intercepts eligible method calls made through the proxy. In particular, a method calling another method internally on the same object usually bypasses the proxy, so that internal invocation is not advised through the normal proxy mechanism.

Practical Considerations

  • Join point versus pointcut: The join point is the actual execution point; the pointcut describes which join points should be affected.

  • Proxy boundaries: Only calls routed through the Spring AOP proxy are intercepted.

  • Self-invocation: Calling an advised method with this.method() inside the same bean generally does not trigger the advice.

  • Final methods and visibility: Proxy-based interception may not work for final methods or methods that the proxy mechanism cannot override.

  • Design guidance: Prefer separate beans and explicit collaboration when advice must consistently apply across internal calls.

9

How does the `@Aspect` annotation serve in Spring AOP, and how is it combined with pointcuts and advice?

The @Aspect annotation marks a class as an aspect, allowing Spring AOP to apply advice to selected join points. An aspect combines a pointcut, which identifies the methods to intercept, with advice, which defines the behavior executed around those methods.

Read full answer

Applies toSpring Framework 6.2 and Spring Boot 3.5 on Java 17+

Purpose of @Aspect

@Aspect comes from org.aspectj.lang.annotation.Aspect and provides metadata that Spring’s aspect-processing infrastructure recognizes. Merely adding the annotation does not define an interception policy; the class normally also contains:

  • Pointcut: Identifies the methods or execution paths to target.

  • Advice: Specifies what code runs before, after, or around the target method.

  • Join point: In Spring AOP, normally an intercepted method execution.

  • Spring bean: The aspect must be registered so the AOP infrastructure can discover it.

The annotation enables annotations such as @Before, @After, @Around, and @Pointcut to be processed as Spring AOP advice.

How the Execution Flow Works

  1. Spring discovers the aspect as a bean.

  2. It evaluates the aspect’s pointcut expressions.

  3. A proxy intercepts calls to matching Spring bean methods.

  4. The advice executes according to its type.

  5. The target method runs, and Spring returns its result or propagates its exception.

Spring AOP is proxy-based. Calls made through the proxy are intercepted, but a method calling another method on itself may bypass the proxy and therefore may not trigger the advice.

Common Advice Types

Annotation When It Executes Important Behavior
@Before Before the target method Cannot replace the method’s return value.
@AfterReturning After successful completion Can inspect the returned value.
@AfterThrowing After an exception is thrown Can inspect the thrown exception.
@After After completion, whether successful or exceptional Often used for cleanup or logging.
@Around Before and after the target method Must call proceed() to invoke the target; can replace its result or exception.

An @Around method is especially powerful because it controls whether and how the target method executes.

Code Example

Add the spring-boot-starter-aop Maven dependency. This starter includes the required AspectJ support and enables Spring Boot’s AOP auto-configuration when the aspect is discovered as a bean.

GreetingService.java:

package com.example.demo;

import org.springframework.stereotype.Service;

@Service
public class GreetingService {

    public String greet(String name) {
        return "Hello, " + name;
    }
}

LoggingAspect.java:

package com.example.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger log =
            LoggerFactory.getLogger(LoggingAspect.class);

    @Pointcut("execution(* com.example.demo.GreetingService.greet(..))")
    public void greetingMethods() {
    }

    @Around("greetingMethods()")
    public Object logInvocation(ProceedingJoinPoint joinPoint)
            throws Throwable {
        log.info("Entering {}", joinPoint.getSignature().toShortString());

        try {
            return joinPoint.proceed();
        } finally {
            log.info("Leaving {}", joinPoint.getSignature().toShortString());
        }
    }
}

Calling greetingService.greet("Mina") through the injected Spring bean causes the @Around advice to execute. The call to joinPoint.proceed() invokes the actual target method.

Limitations

  • Spring AOP intercepts method executions on Spring-managed beans, not arbitrary object construction or field access.

  • Self-invocation can bypass interception because the call does not pass through the proxy.

  • Advice generally cannot intercept private, final, or static methods through Spring’s normal proxy mechanism.

  • Spring AOP and full AspectJ are different. Spring AOP uses proxies, while AspectJ supports compiler- or load-time weaving and a broader range of join points.

  • Keep pointcuts specific to avoid accidentally applying expensive or security-sensitive advice to unrelated methods.

Best Practices

  • Register aspects as Spring beans, commonly with @Component.

  • Prefer constructor injection for dependencies used by an aspect.

  • Use narrowly scoped pointcuts and verify them against known business operations.

  • Call proceed() exactly once in an @Around advice when the target should execute.

  • Preserve exceptions unless the aspect has a deliberate reason to translate or suppress them.

  • Avoid logging sensitive request data, credentials, tokens, or personal information.

10

What configuration and dependencies are required to activate Spring AOP in a Spring application?

In Spring Boot, enable Aspect-oriented programming (AOP) by adding spring-boot-starter-aop and registering an @Aspect component. Spring Boot then automatically configures the auto-proxy infrastructure required to apply the aspect’s advice.

Read full answer

Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17+

How It Works

Spring AOP uses proxy-based interception:

  1. The application registers an aspect containing advice, such as @Around, @Before, or @After.

  2. Spring creates a proxy around a matching bean.

  3. Calls made through that proxy are intercepted and the advice executes.

  4. The proxy delegates the actual business logic to the target bean.

This is Spring AOP rather than AspectJ load-time or compile-time weaving. Consequently, only calls that pass through the Spring-managed proxy are advised.

Required Dependency

For a Spring Boot application, add the following dependency to pom.xml:

  • Maven dependency: org.springframework.boot:spring-boot-starter-aop

The Spring Boot parent or dependency-management configuration supplies the compatible version. The starter brings in Spring AOP and the required AspectJ support.

Do not add @EnableAspectJAutoProxy to a Spring Boot application under normal circumstances. AopAutoConfiguration enables the necessary auto-proxy support when the AOP starter and advice are present.

Code Example

Create the following class in src/main/java/com/example/demo/TimingAspect.java:

package com.example.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimingAspect {

    private static final Logger log = LoggerFactory.getLogger(TimingAspect.class);

    @Around("@within(org.springframework.stereotype.Service)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint)
            throws Throwable {
        long startedAt = System.nanoTime();

        try {
            return joinPoint.proceed();
        } finally {
            long elapsedNanos = System.nanoTime() - startedAt;
            log.info(
                    "{} completed in {} ms",
                    joinPoint.getSignature().toShortString(),
                    elapsedNanos / 1_000_000.0);
        }
    }
}

A business service in src/main/java/com/example/demo/GreetingService.java can then be advised automatically:

package com.example.demo;

import org.springframework.stereotype.Service;

@Service
public class GreetingService {

    public String greet(String name) {
        return "Hello, " + name;
    }
}

The @within pointcut selects methods declared by classes annotated with @Service.

Using Spring Framework Without Boot

For a non-Boot Spring Framework application, include spring-aop and aspectjweaver, then enable auto-proxying explicitly:

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
}

The aspect still needs to be registered as a Spring bean, commonly with @Aspect and @Component.

Best Practices and Common Pitfalls

  • Use narrow pointcuts: Prefer a specific annotation such as a custom @Audited marker or a clearly defined package. Avoid advising every public method unless that is intentional.

  • Keep advice concerns focused: Logging, transactions, security, caching, and metrics are typical cross-cutting concerns.

  • Remember self-invocation limitations: Calling an advised method internally with this.method() bypasses the proxy, so the advice does not execute.

  • Do not rely on private or final-method interception: Spring’s normal proxy mechanisms cannot advise private methods, and class-based proxies cannot intercept final methods.

  • Inject dependencies into aspects through constructors: Follow constructor injection rather than field-level @Autowired.

  • Test through Spring-managed beans: A direct new GreetingService() call bypasses both the proxy and its aspect.

11

How does Spring integrate with relational databases for persistence and query execution?

Spring integrates with relational databases through a DataSource and a database-abstraction layer such as Spring JDBC, Spring Data JPA, or Spring Data JDBC. These components obtain connections from a pool, execute statements, map results into application objects, and coordinate transactions without exposing JDBC details throughout the application.

Read full answer

Applies toSpring Boot 4.0, Spring Framework 7.0, Spring Data JPA 4.0, Spring Data JDBC 4.0, Spring Data R2DBC 4.0, Hibernate ORM 7.1, and Java 17+

How It Works

  • DataSource: Spring Boot configures a DataSource from spring.datasource.* properties, normally using HikariCP as the connection pool.

  • Query execution: Spring JDBC uses JDBC statements with parameter binding and result mapping. Spring Data JPA delegates persistence operations to a JPA EntityManager, normally implemented by Hibernate ORM.

  • Object mapping: A Spring JDBC RowMapper converts result-set rows into Java objects, whereas JPA maps rows to managed entities and supports relationships, persistence contexts, and lazy loading.

  • Connection management: Spring acquires connections when required and returns them to the pool afterward. A transactional resource, such as a JDBC Connection or JPA EntityManager, is bound to the executing thread for the transaction’s duration.

  • Exception translation: SQLException is translated into Spring’s DataAccessException hierarchy. Spring Data repositories translate recognized persistence failures into exceptions such as DataIntegrityViolationException; unrecognized Jakarta Persistence exceptions may still propagate.

  • Boot auto-configuration: When the required dependencies are present, Boot can configure the DataSource, JPA EntityManagerFactory, transaction manager, and Spring Data repositories largely through dependencies and properties.

Database Access Options

Approach Best suited for Main characteristics
Spring JDBC with JdbcTemplate or JdbcClient Explicit SQL and straightforward CRUD Handles connections, parameter binding, result mapping, and exception translation
Spring Data JPA with Hibernate Relationship-rich domain models Provides entities, repositories, JPQL, Criteria queries, lazy loading, and persistence contexts
Spring Data JDBC Simple aggregate persistence Maps aggregates directly without a full entity-management context
Spring Data R2DBC Reactive applications Uses non-blocking drivers and reactive connection pools; it does not use JDBC

Spring JDBC is useful when precise control over SQL is important. Spring Data JPA is generally preferable when relationships and an object-oriented domain model are central to the application.

Transactions and Persistence

@Transactional delegates transaction creation, commit, rollback, and resource cleanup to the applicable PlatformTransactionManager.

  • A JDBC-only stack normally uses DataSourceTransactionManager; JDBC operations using that manager reuse its transaction-bound connection.

  • A JPA stack normally uses JpaTransactionManager; JPA operations join its transaction-bound EntityManager and persistence context.

  • In applications mixing JPA and JDBC, transaction managers and enlistment must be configured deliberately rather than assuming that every operation participates in the same transaction.

  • Spring’s proxy-based transaction interception means self-invocation within the same object does not activate @Transactional.

  • Declared Spring Data JPA query methods are not transactional merely because they are repository methods. Inherited CRUD methods are transactional, while custom write methods should declare @Transactional.

  • Keep transactions short and avoid network calls or lengthy processing while a database transaction is open.

  • Accessing a lazy JPA relationship after its EntityManager or persistence context has closed can cause LazyInitializationException. Fetch joins, projections, or an appropriate transaction boundary are safer.

Schema Management and Query Safety

Production schemas should normally be managed with repeatable migrations from a dedicated tool such as Flyway or Liquibase rather than ad hoc table creation.

  • Spring Data derived queries bind method arguments automatically.

  • JPA queries should use named or positional parameters, and Spring JDBC queries should use placeholders.

  • Never construct SQL by concatenating request data, even when manually attempting to escape it.

  • Validate input, enforce schema-level length and nullability constraints, and use database-generated identifiers where appropriate.

Best Practices

  • Validate external input before entering transactional business operations.

  • Use explicit SQL for complex or performance-sensitive operations and inspect generated SQL during testing.

  • Avoid N+1 queries; use fetch joins, entity graphs, or projections where appropriate.

  • Size and monitor the connection pool, including active, idle, and pending connections.

  • Test against the same database engine used in production.

  • Keep database credentials out of source code; use environment-specific secrets or configuration providers.

12

Which interfaces are central to performing JDBC work in Spring?

Spring’s JDBC support centers on JdbcOperations and NamedParameterJdbcOperations. A DataSource supplies connections, while callback and strategy interfaces define statement preparation, parameter binding, row processing, and exception translation; classes such as JdbcTemplate implement these contracts.

Read full answer

Applies toSpring Framework 6.2 and Spring Boot 3.4 (Java 17+; Spring web and persistence APIs use `jakarta.*`, while the standard JDBC `DataSource` remains in `javax.sql`)

Central Interfaces

  • DataSource: The standard JDBC connection source. A DataSource implementation such as HikariCP manages physical connections, while JdbcTemplate obtains a connection and releases it back to the DataSource after execution.

  • JdbcOperations: The main contract for common JDBC operations. JdbcTemplate is its standard implementation and can be injected through the interface when only the abstraction is needed.

  • NamedParameterJdbcOperations: Supports named parameters such as :customerId. NamedParameterJdbcTemplate is the standard implementation and delegates the underlying work to JdbcOperations.

  • JdbcClient: A fluent interface introduced in Spring Framework 6.1. It provides a modern API for parameter binding and querying while retaining the behavior and resource management of JdbcTemplate.

  • RowMapper: Converts one current row of a ResultSet into an application object. It is the usual choice for queries returning multiple objects.

  • ResultSetExtractor: Receives the open ResultSet and produces one result while controlling iteration itself. It is appropriate for aggregates or results that cannot be represented one row at a time.

  • RowCallbackHandler: Processes each row and returns no result. The interface does not require the callback to be stateless.

  • SQLExceptionTranslator: Translates JDBC-specific SQLExceptions into Spring’s unchecked DataAccessException hierarchy, including exceptions such as BadSqlGrammarException and DuplicateKeyException.

Supporting Extension Points

  • PreparedStatementCreator: Creates and configures a PreparedStatement, which is useful when generated keys or vendor-specific statement features are required.

  • PreparedStatementSetter: Binds values to a PreparedStatement that the template has already created.

  • CallableStatementCreator: Creates a CallableStatement for stored-procedure execution, as used by SimpleJdbcCall.

  • BatchPreparedStatementSetter: Defines the batch size and binds each item’s values for batchUpdate.

  • SqlParameterSource: Supplies named parameter values to NamedParameterJdbcOperations. Common implementations include MapSqlParameterSource and BeanPropertySqlParameterSource.

  • ConnectionCallback and StatementCallback: Low-level callback interfaces for code that needs direct access to a managed Connection or Statement.

How They Fit Together

  1. A repository or service obtains a JDBC template backed by a DataSource.

  2. The template obtains a connection through the DataSource and participates in any applicable transaction synchronization.

  3. A statement creator, parameter setter, or direct parameter overload prepares the SQL statement.

  4. The statement executes, and its rows are processed through a RowMapper, ResultSetExtractor, or RowCallbackHandler.

  5. Spring closes statement and result-set resources and translates JDBC failures through its SQLExceptionTranslator.

Selecting a Row-Processing Strategy

Interface Processing model Typical result Typical use
RowMapper One row at a time One object per row Queries returning a list of entities
ResultSetExtractor Controls iteration over the result set Any result Aggregates, joins folded into one object, or custom multi-row processing
RowCallbackHandler One row at a time No returned value Side effects for each row, such as writing records

Practical Guidance

  • Prefer constructor injection of JdbcOperations, NamedParameterJdbcOperations, JdbcClient, or their template implementations. Spring Boot normally supplies the required template beans.

  • Use bound parameters and explicit SQL types where the API permits them; never concatenate untrusted input into SQL.

  • Let the template manage connections, statements, result sets, and transaction-aware resource handling.

  • Treat JdbcTemplate as thread-safe only when its DataSource supports concurrent access.

  • Check the row count returned by an update. queryForObject throws EmptyResultDataAccessException when no result is found, so use a list-based query when an empty result is valid.

  • Choose batch sizes according to the JDBC driver’s limits and the application’s memory characteristics rather than relying on a fixed universal value.

13

How does the Spring MVC framework fit into the development of Spring-based web applications?

Spring MVC is Spring’s servlet-based web framework for handling HTTP requests, binding request data, invoking application logic, and producing HTML or structured responses. It provides a model-view-controller architecture while also supporting REST APIs through JSON and other content types.

Read full answer

Applies toSpring Framework 6.2 with Spring Boot 3.5 on Java 17+

Core Role

Spring MVC is a module within Spring Framework. It builds on Spring’s dependency-injection container and the Jakarta Servlet API to provide a controller-oriented approach to web development.

Its central component is DispatcherServlet, which acts as the front controller for incoming servlet requests. It coordinates a processing pipeline rather than implementing every responsibility itself.

A typical request passes through these stages:

  1. DispatcherServlet receives the request.

  2. A HandlerMapping identifies the matching controller method.

  3. A HandlerAdapter invokes that method with bound arguments.

  4. The returned result is either:

  5. Rendered as a view, such as a server-side HTML template.

  6. Serialized into JSON or another media type.

  7. A HandlerExceptionResolver handles exceptions and error responses.

Request Handling and Data Binding

Controllers use annotations such as @Controller, @RequestMapping, and @GetMapping to declare routes. Specialized annotations such as @PostMapping, @PutMapping, and @DeleteMapping restrict a route to a particular HTTP method.

Spring MVC can bind values from different request sources:

  • @PathVariable: A path segment, such as the id in /orders/{id}.

  • @RequestParam: A query-string or form parameter.

  • @RequestBody: A deserialized request payload, commonly JSON.

  • @RequestHeader: An HTTP header.

  • @CookieValue: A cookie value.

  • @ModelAttribute: A form-backed object or model attribute.

Bean Validation annotations such as @NotBlank, @Size, and @Positive can validate bound input. Controllers may also accept HttpServletRequest, HttpServletResponse, and Spring MVC’s Model when lower-level access is required.

MVC Views and REST Responses

For traditional server-rendered applications, a controller returns a logical view name and model attributes. A ViewResolver then selects the view implementation, while Spring’s validation and data-binding support populate the model.

For REST APIs, @RestController combines @Controller and @ResponseBody. The returned object is serialized by an HTTP message converter, typically producing JSON.

For example, a GET /orders/42 route can return an OrderResponse object without manually writing JSON. Spring MVC handles content negotiation, serialization, and status selection through framework components.

Relationship With Spring Boot and Spring Security

Spring MVC defines the web-application framework, while Spring Boot supplies configuration, dependency management, and auto-configuration. The spring-boot-starter-web starter provides Spring MVC and an embedded servlet container such as Tomcat by default. Spring MVC can also be used without Spring Boot.

Spring Security integrates with the servlet-based stack through a SecurityFilterChain bean. Security filters execute before the request reaches DispatcherServlet, so authentication and authorization remain separate from controller logic.

Advantages and Limitations

  • Mature ecosystem: Spring MVC integrates with Spring Security, validation, transactions, testing, and the wider Spring ecosystem.

  • Broad use cases: It supports server-rendered applications, REST APIs, form processing, file uploads, and asynchronous request handling.

  • Clear separation: Controllers coordinate transport concerns, while services and repositories contain business and data-access logic.

  • Servlet-oriented model: It is based on the Jakarta Servlet API and blocking I/O. For highly concurrent, stream-oriented workloads, WebFlux may be more appropriate.

  • Not an HTTP client: Spring MVC receives inbound requests. For outbound calls, prefer RestClient or WebClient; RestTemplate is in maintenance mode.

Best Practices

  • Use constructor injection for controller dependencies.

  • Keep controllers thin and place business rules in service classes.

  • Use DTOs rather than exposing persistence entities directly.

  • Validate all external input with Bean Validation.

  • Handle expected failures with @ExceptionHandler or @ControllerAdvice, using ProblemDetail for consistent API error responses.

  • Test web-layer behavior with @WebMvcTest and integration tests where infrastructure interactions must also be verified.

  • Return accurate HTTP status codes and avoid exposing stack traces or sensitive information to clients.

14

Explain how the components of a Spring MVC application are organized and interact with each other.

A Spring MVC application follows the front controller pattern, where a DispatcherServlet handles requests routed to it and coordinates components such as HandlerMapping, HandlerAdapter, controllers, message converters, and view resolution. This separates HTTP request handling, business logic, and response rendering.

Read full answer

Applies toSpring Framework 6 with Spring Boot 3 (Java 17+)

How the Request Flows

  1. The client sends an HTTP request, and the container routes it to the DispatcherServlet according to the servlet mapping.

  2. A HandlerMapping inspects the request and returns a HandlerExecutionChain containing the selected controller handler and any applicable interceptors.

  3. A HandlerAdapter invokes the selected controller method. Argument resolvers support parameters such as @RequestParam, @PathVariable, @RequestBody, and validated request objects.

  4. The controller performs the request-specific work, often by calling a service, and returns a result. Depending on the controller type, this can be a ModelAndView, a logical view name, a response body, or a ResponseEntity.

  5. If an exception occurs, HandlerExceptionResolver implementations can handle it, including through @ExceptionHandler methods or other configured exception strategies.

  6. For a view-based response, a ViewResolver resolves the logical view name to a View, which renders the model into the response. For @ResponseBody or @RestController methods, HttpMessageConverter implementations instead serialize the returned value directly to the response body.

  7. HandlerInterceptor callbacks run before handler invocation, after handler invocation, and after response completion.

Core Components

  • DispatcherServlet: The front controller that coordinates the MVC request lifecycle. Spring Boot auto-registers it for web applications; it can also be customized through Boot’s servlet registration support or deployment descriptor configuration.

  • HandlerMapping: Selects a handler method from the request. RequestMappingHandlerMapping supports annotation-based mappings such as @GetMapping and @PostMapping.

  • HandlerAdapter: Invokes the selected handler and adapts its result. RequestMappingHandlerAdapter is the standard adapter for annotation-driven controllers.

  • Controller handlers: Usually classes annotated with @Controller or @RestController. The older org.springframework.web.servlet.mvc.Controller interface is deprecated in favor of annotated controllers.

  • Model: Holds request attributes intended for a view. It can be supplied as a Model, ModelMap, or through the ModelAndView returned by a controller.

  • ViewResolver and View: Resolve logical view names and render them. Common implementations include InternalResourceViewResolver for JSP and ThymeleafViewResolver for Thymeleaf templates.

  • HttpMessageConverter: Converts between HTTP message bodies and Java types. Jackson-based converters, when configured, support JSON and XML representations.

  • HandlerExceptionResolver: Locates an appropriate exception-handling strategy and may produce a normal response, an alternate view, or an error result.

MVC and REST Responses

Concern View-oriented MVC REST-style MVC
Controller annotation @Controller @RestController
Typical result Logical view name or ModelAndView Java object, ResponseEntity, or another response body
Rendering component ViewResolver and View HttpMessageConverter
Main representation HTML or another server-rendered format Commonly JSON, although XML is also possible

Best Practices

  • Keep controllers thin: handle HTTP concerns in the controller, business logic in services, and persistence logic in repositories.

  • Use constructor injection for dependencies rather than field injection with @Autowired.

  • Prefer DTOs over exposing persistence entities directly from controllers, and validate external input with Bean Validation.

  • Configure JSON and XML explicitly through supported Spring mechanisms rather than relying on accidental classpath behavior.

  • Prefer annotation-driven Java configuration in new applications, although XML configuration remains supported in Spring Framework 6 and Spring Boot 3.

  • Keep view-oriented controllers and REST APIs separate when their audiences, error models, or security policies differ.

15

What purpose do the @Controller and @RequestMapping annotations serve in Spring MVC?

The @Controller annotation marks a class as a Spring MVC request handler component, while @RequestMapping defines the HTTP request patterns that the class or its methods should handle. Together they form the foundation of Spring’s annotation-driven web MVC mapping.

Read full answer

Applies toSpring Framework 7.0 with Spring Boot 4.0 on Java 17+

How They Work Together

@Controller and @RequestMapping are complementary annotations. The controller annotation identifies the class as a request handler, and the mapping annotation specifies which requests that handler should process. Without @Controller, a class annotated only with @RequestMapping would not be discovered as a Spring bean by component scanning, because @RequestMapping is not a component stereotype. If such a class is registered as a bean through another mechanism, however, Spring MVC can still detect its @RequestMapping methods. The annotations live in the org.springframework.stereotype and org.springframework.web.bind.annotation packages, respectively.

Role of @Controller

  • Component detection: It is a specialization of @Component, so annotated classes are picked up automatically during component scanning.

  • Handler identification: It identifies the class as a Spring MVC controller whose methods may handle HTTP requests.

  • No mapping logic: The annotation itself does not define request paths or HTTP methods; @RequestMapping supplies that mapping information.

  • Standalone use: A class marked with @Controller can still operate as a Spring bean if it has no mapping annotations.

Role of @RequestMapping

  • Path mapping: It maps a class-level base path or a method-level specific path to the handler method.

  • HTTP method filtering: The method attribute restricts which HTTP verbs, such as GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, or TRACE, the handler accepts.

  • Request constraints: It supports params, headers, consumes, and produces attributes for fine-grained request matching.

  • Class and method level: A class-level mapping provides the base path, and a method-level mapping is combined with it to identify the complete endpoint mapping.

Best Practices

  • Use @Controller only for web MVC controllers; use @RestController for REST APIs whose handler methods return response bodies directly.

  • Keep class-level @RequestMapping paths short and logical, such as /users, and let method-level mappings define specific endpoints.

  • Prefer the path attribute over value for readability, though both are supported aliases in Spring Framework 7.0.

  • Avoid overusing @RequestMapping constraints; apply them only when they are necessary to express the endpoint’s contract.

16

Describe what the DispatcherServlet is and what responsibilities it handles inside Spring MVC.

The DispatcherServlet is the front controller of Spring MVC: a single servlet that receives every HTTP request routed to the application, delegates the work to configurable strategy beans, and writes the response. Instead of many servlets each owning a URL, one dispatcher servlet owns the entry point and orchestrates handler lookup, argument binding, invocation, view rendering and error handling.

Read full answer

Applies toSpring Framework 6.x with Spring Boot 3.x on Java 17+ (`jakarta.servlet.*` APIs).

How It Works

DispatcherServlet extends FrameworkServlet, which extends HttpServletBean and ultimately jakarta.servlet.http.HttpServlet. It overrides the servlet service/doGet/doPost methods so that all HTTP verbs funnel into a single doDispatch method.

Rather than hard-coding behaviour, it delegates to strategy interfaces that are looked up from the application context (via DispatcherServlet.properties defaults, WebMvcAutoConfiguration in Boot, or explicit bean definitions):

  • HandlerMapping: maps the request (path, method, headers) to a handler plus its interceptors. In Boot 3 the primary one is RequestMappingHandlerMapping, built from @Controller classes and @RequestMapping / @GetMapping methods; resource and welcome-page mappings are registered too.

  • HandlerAdapter: knows how to actually invoke the chosen handler. RequestMappingHandlerAdapter drives annotated controller methods.

  • HandlerMethodArgumentResolver / HandlerMethodReturnValueHandler: bind method parameters from the request and turn return values into responses.

  • ViewResolver: turns a logical view name into a concrete View (Thymeleaf, JSP, etc.). Boot adds a ContentNegotiatingViewResolver.

  • HandlerExceptionResolver: converts thrown exceptions into responses (ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver).

  • LocaleResolver, ThemeResolver, MultipartResolver, FlashMapManager: locale detection, themes, multipart parsing (StandardServletMultipartResolver when multipart is enabled), and flash attributes across redirects.

Request Lifecycle

  1. The servlet container calls service; FrameworkServlet resolves multipart content and publishes ServletRequestHandledEvent.

  2. doDispatch iterates the HandlerMapping beans until one returns a HandlerExecutionChain for the request.

  3. A matching HandlerAdapter is selected; HandlerInterceptor.preHandle runs for each interceptor.

  4. The adapter runs argument resolvers, invokes the controller method, then runs return value handlers and postHandle.

  5. A ModelAndView goes to the ViewResolver and the view renders the response; a @ResponseBody / ResponseEntity return value is written directly by an HttpMessageConverter (Jackson for JSON), bypassing view resolution.

  6. afterCompletion runs, and any exception along the way is passed to the exception resolvers.

  7. The response is committed and the request completed; for async return types the servlet thread is released and AsyncContext completes it later.

Async and Error Handling

  • Returning Callable, DeferredResult, WebAsyncTask or a reactive type starts async processing through WebAsyncManager; interceptors get the postHandle/afterCompletion callbacks on the async dispatch.

  • @ExceptionHandler methods, @ControllerAdvice classes and @ResponseStatus are all resolved by the dispatcher’s exception resolvers, so a dedicated error @Controller or ProblemDetail response can be returned consistently.

Best Practices and Common Pitfalls

  • Keep controllers thin: business logic belongs in services; the dispatcher is an orchestration layer, not a place for logic.

  • Do not subclass or replace DispatcherServlet for cross-cutting concerns; use HandlerInterceptor, Filter (OncePerRequestFilter) or an @ControllerAdvice instead.

  • Prefer @RestController (or @ResponseBody) for HTTP APIs so responses skip view resolution, and rest-style ResponseEntity for explicit status codes.

  • Avoid @EnableWebMvc in Boot unless you deliberately want to switch off Boot’s MVC auto-configuration, which also disables converters, formatters and static resource handling.

  • Remember Boot’s default: DispatcherServletAutoConfiguration registers one dispatcher servlet at the path in spring.mvc.servlet.path (default /). Multiple dispatcher servlets at different paths are possible but rarely needed; a single one plus path prefixes is simpler.

  • Message conversion and validation happen inside the dispatcher, so a missing @Valid or a misconfigured converter shows up as a 400 rather than an exception reaching your code.

17

How does Spring MVC process an incoming web request, from arrival to response?

Spring MVC routes every web request through DispatcherServlet, a front controller that finds a handler method on a controller, binds request data to its arguments, invokes it, and converts the return value into an HTTP response.

Read full answer

Applies toSpring Boot 3.5 (Spring Framework 6.2) on Java 17+, using `jakarta.servlet` and `spring-boot-starter-web`

Request Lifecycle Through DispatcherServlet

  1. The servlet container (Tomcat by default) receives the request. Spring Boot maps DispatcherServlet to /; the mapping can be changed with spring.mvc.servlet.path.

  2. DispatcherServlet asks its HandlerMapping implementations for a handler. RequestMappingHandlerMapping handles annotated controllers and returns a HandlerExecutionChain containing the handler plus matching interceptors. Since Spring 6, PathPatternParser is the default path matcher.

  3. A HandlerAdapter (RequestMappingHandlerAdapter) invokes the method. It resolves each argument with a HandlerMethodArgumentResolver and applies validation where requested.

  4. The return value goes through a HandlerMethodReturnValueHandler. @ResponseBody and ResponseEntity results are written by an HttpMessageConverter such as Jackson; a plain String from a @Controller without @ResponseBody is treated as a view name and resolved by a ViewResolver.

  5. Exceptions from handler processing go through the HandlerExceptionResolver chain: @ExceptionHandler methods first, then Spring’s built-in resolvers such as ResponseStatusExceptionResolver and DefaultHandlerExceptionResolver; unresolved errors reach Boot’s /error handling.

  6. HandlerInterceptor#afterCompletion callbacks run after request processing, including after view rendering or an exception. The response may already have been committed by then.

Handlers and Input Binding

  • @Controller: methods usually return view names; add @ResponseBody to write data directly.

  • @RestController: combines @Controller and @ResponseBody, so each method’s return value is serialised.

  • Mapping annotations: @RequestMapping, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping map URLs and can be narrowed with params, headers, consumes and produces.

  • @PathVariable: reads a URI template variable. @RequestParam reads a query parameter and supports required = false and defaultValue.

  • @RequestBody: deserialises the body with an HttpMessageConverter (Jackson for JSON). Add @Valid to trigger Bean Validation; failures raise MethodArgumentNotValidException and normally produce a 400. This requires spring-boot-starter-validation.

  • @ModelAttribute, @RequestHeader, @CookieValue, @RequestPart: bind form objects, headers, cookies and multipart parts.

  • Type conversion: ConversionService plus @DateTimeFormat/@NumberFormat convert strings to types such as LocalDate and enums.

Producing the Response

  • Return a DTO: with @RestController, the object is serialised and the status defaults to 200.

  • ResponseEntity<T>: controls status, headers and body, e.g. ResponseEntity.notFound().build().

  • @ResponseStatus: sets a fixed status such as HttpStatus.CREATED for a handler method.

  • ProblemDetail: a problem+json error payload supported since Spring 6; return it from an @ExceptionHandler.

  • Content negotiation: the Accept header and produces conditions select the representation.

Cross-Cutting Concerns

Mechanism Use it for
Filter / OncePerRequestFilter Servlet-level work outside MVC: CORS, logging, auth tokens
HandlerInterceptor MVC-aware pre/post processing with access to the chosen handler
@RestControllerAdvice + @ExceptionHandler Translating exceptions into API error responses
ResponseEntityExceptionHandler Extending it to customise Spring’s built-in MVC exception handling

Register interceptors by implementing WebMvcConfigurer#addInterceptors.

Best Practices and Pitfalls

  • Keep controllers thin: validate input, delegate to services and map to DTOs; do not expose entities directly.

  • Prefer @RestController for APIs and @Controller only when rendering server-side views such as Thymeleaf.

  • Return ResponseEntity or ProblemDetail rather than letting raw exception messages leak to clients.

  • Do not perform blocking I/O or long transactions in a filter or interceptor; they run for every request.

  • Remember that @Valid on @RequestBody fails before your method body executes, so the response comes from exception handling.

  • For streaming or fully non-blocking workloads, use WebFlux (spring-boot-starter-webflux) instead of MVC.

19

How do `@Controller` and `@RestController` differ in Spring MVC, and how does each type affect the processing of handler-method return values?

@Controller and @RestController both identify Spring MVC request handlers, but they differ in their default response behavior. @RestController is effectively @Controller plus @ResponseBody, so its handler methods normally write directly to the HTTP response body rather than resolving a view.

Read full answer

Applies toSpring Framework 7.0, Spring Boot 4.0, Java 17+

Core Difference

  • @Controller: Marks a class as a Spring MVC controller. Handler methods may return:

  • A logical view name, such as "home", which is resolved by a configured ViewResolver.

  • A response body when the method or class is annotated with @ResponseBody.

  • A framework result type such as ResponseEntity.

  • @RestController: Marks a class as a controller whose handler methods produce HTTP response bodies. It combines the behavior of @Controller and @ResponseBody at the class level.

The important distinction is not URL mapping or component registration; both annotations make the annotated class eligible for request handling through @RequestMapping and related annotations.

Return-Value Processing

Handler declaration Typical return value Result
@Controller "home" Treated as a view name and resolved by a ViewResolver
@Controller Java object without @ResponseBody Also treated as a view name, which usually causes an error unless a suitable view exists
@Controller with @ResponseBody Java object Serialized into the response body
@RestController Java object Serialized into the response body
@RestController ResponseEntity<T> Status, headers, and body are written directly to the response

In a @RestController, returning "home" produces the literal text home in the response body; it does not select a server-side view.

How Serialization Works

For a serializable response-body return value, Spring MVC selects a compatible HttpMessageConverter:

  • In Spring Framework 7.0 and Spring Boot 4.0, JSON is handled with Jackson 3 through JacksonJsonHttpMessageConverter. MappingJackson2HttpMessageConverter remains available for Jackson 2 but is deprecated in Spring Framework 7.0.

  • XML can be handled by an XML converter when the required library and converter are configured.

  • Byte arrays, strings, and other supported types have their own converters.

The selected representation depends on the response content type and HTTP content negotiation. With Spring Boot’s Jackson HTTP message-converter auto-configuration active, a returned object is commonly serialized as JSON. In plain Spring Framework, Jackson being on the classpath alone does not register a converter.

@RestController does not itself impose JSON as the only supported format, nor does it create a REST architecture automatically. It primarily changes how handler return values are interpreted.

Response Status and Headers

Both controller styles can use ResponseEntity<T> to control the complete HTTP response:

  • Status code: For example, 201 Created or 404 Not Found.

  • Headers: Such as Location, Cache-Control, or Content-Type.

  • Body: A serialized response object or another supported type.

Method-level annotations such as @ResponseStatus can also set the response status when a ResponseEntity is unnecessary.

Best Practices and Common Pitfalls

  • Use @RestController for JSON, XML, or other body-oriented web APIs.

  • Use @Controller for server-rendered applications that resolve views.

  • Add @ResponseBody to a specific @Controller method when only that operation should return a response body.

  • Avoid relying on a ViewResolver in an API controller; returned strings may be mistaken for data.

  • Do not confuse @RestController with @ResponseStatus: the former controls result processing, while the latter primarily controls status selection.

  • Do not assume @RestController provides validation, security, or automatic error-handling responses. Those concerns are handled separately with mechanisms such as jakarta.validation, Spring Security, and @ControllerAdvice.

20

What are the steps to build a Spring Boot application from scratch?

A Spring Boot application is created by generating a project skeleton, usually with Spring Initializr, adding the required starters to the build, and writing a class annotated with @SpringBootApplication whose main method delegates to SpringApplication.run.

Read full answer

Applies toSpring Boot 3.5 (Spring Framework 6.2) on Java 17 or later

Generating the Project

  1. Open the Spring Initializr web service, use an IDE’s Spring Initializr wizard, or call its API with curl.

  2. Install a JDK 17 or later, then choose Maven or Gradle, Java or Kotlin, and Java 17 or a later release. Spring Boot 3.x requires Java 17 as its baseline.

  3. Choose Jar packaging and include spring-boot-starter-web for the example below. Other useful starters include spring-boot-starter-data-jpa, spring-boot-starter-validation, spring-boot-starter-security, spring-boot-starter-actuator, and spring-boot-starter-devtools.

  4. Download, unzip, and open the project. Initializr includes the Maven wrapper (mvnw) or Gradle wrapper (gradlew), so a separate build-tool installation is unnecessary.

  5. The generated build declares spring-boot-starter-parent with Maven or applies the Spring Boot and dependency-management plugins with Gradle. It also includes spring-boot-starter-test in test scope. Managed dependency versions normally need no explicit <version>.

The default layout includes src/main/java, src/main/resources, static, templates, and src/test/java.

The Application Class, Starters and Auto-Configuration

  • @SpringBootApplication: combines @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan. Place it in the application’s top-level package so component scanning covers the intended subpackages.

  • Starters: curated dependency sets such as spring-boot-starter-web, which includes Spring MVC and embedded Tomcat, and spring-boot-starter-data-jpa, which includes Spring Data JPA and Hibernate support.

  • Auto-configuration: conditionally contributes beans. Many auto-configurations back off when an application supplies a compatible bean, but the exact condition depends on the auto-configuration.

  • Configuration: store settings in application.yaml or application.properties, use profiles for environment-specific overrides, and bind related settings with @ConfigurationProperties. Register configuration-properties classes with @ConfigurationPropertiesScan or @EnableConfigurationProperties.

  • Jakarta namespace: Spring Boot 3 uses jakarta.* for its supported persistence and validation APIs; older Boot 2 tutorials generally use javax.*.

Running, Testing and Packaging

  • Run with ./mvnw spring-boot:run or ./gradlew bootRun, or launch the main method from the IDE.

  • ./mvnw package creates an executable jar when the web starter is present; start it with java -jar. The Spring Boot Maven or Gradle plugin performs the repackaging.

  • Use @SpringBootTest to boot an application context for integration testing. Add @AutoConfigureMockMvc when that test needs MockMvc. Use @WebMvcTest or @DataJpaTest for narrower slices; @WebMvcTest already configures MockMvc.

Code Example

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // Enables configuration, auto-configuration, and component scanning
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args); // Starts embedded Tomcat
    }
}
package com.example.demo.greeting;

import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Service
class GreetingService {
    String greeting() {
        return "Hello from Spring Boot";
    }
}

@RestController
class GreetingController {

    private final GreetingService greetingService;

    // Constructor injection; @Autowired is unnecessary for a single constructor
    GreetingController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    @GetMapping("/greeting")
    String greet() {
        return greetingService.greeting();
    }
}
# src/main/resources/application.yaml
spring:
  application:
    name: demo
server:
  address: 127.0.0.1 # Restrict this local-development example to loopback
  port: 8081
./mvnw spring-boot:run                     # Start locally on port 8081
./mvnw package && java -jar target/demo-0.0.1-SNAPSHOT.jar

Best Practices and Common Pitfalls

  • Constructor injection: declare dependencies as final fields initialized by the constructor. Field injection with @Autowired hides dependencies and prevents immutability.

  • Main-class placement: placing DemoApplication too deep in the package hierarchy can exclude sibling components; correct the package structure rather than adding broad scanBasePackages.

  • Managed versions: avoid overriding Spring Boot’s curated dependency versions unless the application’s compatibility requirements have been tested.

  • Secrets: obtain credentials from environment variables or an external secret source rather than committing them to source code or configuration files.

  • Actuator: use Actuator for operational endpoints instead of creating custom status endpoints. Expose only the required endpoints over HTTP and secure them appropriately; the health endpoint is exposed by default, while metrics require explicit exposure.

  • Transport security: the sample binds to loopback for local development. Configure Spring Boot SSL or terminate HTTPS at a trusted reverse proxy for deployed applications.

  • DevTools: use it for local development only; it is automatically disabled in packaged applications.

  • Namespace migration: update javax.persistence and similar imports to jakarta.persistence when following Boot 2 tutorials with Boot 3.

21

Describe how Spring Boot's auto-configuration mechanism works and what enables it to automatically set up your application.

Auto-configuration is Spring Boot’s mechanism for creating sensible default beans from the dependencies, properties, and application type already present. It reduces the need for manual configuration while still allowing application-specific beans to replace or exclude those defaults.

Read full answer

Applies toSpring Boot 3.5.x with Spring Framework 6.2.x on Java 17+

How It Works

  • @SpringBootApplication includes @EnableAutoConfiguration, which starts the auto-configuration import process.

  • Spring Boot reads candidate auto-configuration classes from META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. In Boot 3.x, this replaces the older META-INF/spring.factories registration mechanism.

  • It does not indiscriminately scan the classpath and turn every configuration class into an auto-configuration. Instead, it evaluates the known candidates against the current application context.

  • Each candidate acts as a configuration class, but its @Bean methods are normally guarded by conditions. Depending on the condition, Boot may create the bean, skip it, or defer the decision until the context contains more bean definitions.

  • Auto-configurations are processed after ordinary application configuration. They can also declare ordering relationships with @AutoConfigureBefore, @AutoConfigureAfter, or @AutoConfigureOrder.

What Enables Automatic Configuration

  • Relevant dependencies: A dependency on a Spring Boot starter often brings in the auto-configuration class and supporting libraries needed for a feature, such as JDBC, web, or transaction support.

  • Application annotations: @SpringBootApplication on the main application class enables component scanning and auto-configuration. @EnableAutoConfiguration can be used directly in specialized bootstrap configurations.

  • Conditions: Boot evaluates classpath presence, configuration properties, application type, and existing beans before registering conditional beans.

  • Configuration properties: Properties under namespaces such as spring.datasource.*, server.*, and spring.jpa.* customize the relevant auto-configured infrastructure.

  • User-defined beans: Auto-configurations commonly use back-off conditions so that an application-defined bean can take precedence.

Conditions and Back-Off

  • @ConditionalOnClass: Applies the configuration when specified classes are available. This is commonly used to avoid loading auto-configuration classes whose dependencies are absent.

  • @ConditionalOnMissingBean: Applies the configuration only when a matching bean definition is not already registered. It is primarily intended for auto-configuration classes.

  • @ConditionalOnBean: Applies the configuration when required beans are already available.

  • @ConditionalOnProperty: Applies the configuration according to the presence, absence, or value of configuration properties.

  • @ConditionalOnWebApplication: Matches applications according to their web-application type, such as servlet or reactive.

Condition evaluation is order-sensitive. Some bean conditions can only be evaluated reliably after other beans have been defined, which is why auto-configurations may use ordering annotations.

Controlling and Diagnosing It

A conflicting auto-configuration can be excluded by naming its configuration class in the exclude attribute of @SpringBootApplication, or by listing its fully qualified class name in the spring.autoconfigure.exclude property. Excluding a class disables all of its contributions, so the application must provide any required replacement beans itself.

The Condition Evaluation Report is available when the application starts with --debug. It shows which auto-configurations matched, which were excluded, and why each condition succeeded or failed. This is usually the fastest way to investigate unexpected defaults.

Best Practices and Common Pitfalls

  • Prefer adding a user-defined bean over excluding an entire auto-configuration when only one default needs to be replaced.

  • Use @ConditionalOnMissingBean in auto-configuration code, not as a general mechanism for declaring application beans.

  • Avoid broad exclusions unless their effects are understood; one auto-configuration may provide several related defaults.

  • Remember that adding or removing a dependency can change which auto-configurations match.

  • Use the --debug condition report instead of guessing why a bean was created or omitted.

22

In what ways does Spring Boot simplify an application’s data-access layer?

Spring Boot simplifies data access through auto-configuration and Spring Data abstractions. It supplies common infrastructure, configures persistence components from dependencies and properties, and reduces repetitive repository and transaction-management code without preventing explicit control over SQL.

Read full answer

Applies toSpring Boot 4.0, Spring Framework 7.0, Spring Data 2025.1, and Java 17+

Dependency Management and Auto-Configuration

  • Dependency-managed starters: Add the relevant starter, such as spring-boot-starter-jdbc, spring-boot-starter-data-jpa, spring-boot-starter-data-mongodb, or spring-boot-starter-data-r2dbc. The Spring Boot BOM coordinates compatible dependency versions.

  • Automatic infrastructure configuration: Conditional auto-configuration detects persistence libraries on the classpath and creates beans such as DataSource, JdbcTemplate, JdbcClient, JPA infrastructure, and transaction managers when appropriate.

  • Pooled connections: For relational databases, spring.datasource.* properties configure the DataSource. HikariCP is the default pool when it is available unless another pool is explicitly selected.

  • Environment-based configuration: Connection details, pool settings, JPA behavior, and initialization options can be supplied through properties, environment variables, and profiles without hard-coding them in application code.

Repository Abstractions and Query APIs

Spring Data creates repository proxies for interfaces that extend its repository abstractions. Depending on the module, these proxies provide CRUD operations, pagination, sorting, derived queries, and support for explicitly declared queries.

Approach Main APIs Best suited for
Plain JDBC JdbcTemplate, JdbcClient, NamedParameterJdbcTemplate Explicit SQL, reporting queries, and complex joins
JPA and Hibernate EntityManager, JpaRepository, JpaSpecificationExecutor Object-oriented relational mappings and domain models
Reactive SQL R2dbcEntityTemplate, reactive Spring Data repositories Non-blocking applications using R2DBC drivers
Document databases Spring Data MongoDB repositories Document-oriented and flexible-schema data
Specialized stores Spring Data Redis and Spring Data Cassandra modules Key-value, wide-column, and specialized persistence models

Boot does not require applications to use JPA. Explicit SQL through JdbcTemplate or JdbcClient remains appropriate when generated queries would obscure the required database behavior.

Schema Initialization and Migrations

  • schema.sql and data.sql can initialize an embedded database automatically. For non-embedded databases, SQL initialization must be enabled explicitly rather than relying on the embedded-database default.

  • Boot also provides auto-configuration for established migration tools such as Flyway and Liquibase.

  • Production applications should normally use versioned migrations instead of schema.sql, ad hoc initialization scripts, or implicit Hibernate schema updates.

  • Test support includes focused slices such as @JdbcTest and @DataJpaTest, which configure only the persistence components required by the test.

Transactions and Error Handling

@Transactional enables declarative transaction management when a suitable transaction manager is available and the call passes through the Spring proxy. Spring begins the transaction before the proxied method executes and commits it after successful completion.

  • The default propagation behavior is REQUIRED: the method joins an existing transaction or starts one when necessary.

  • Unchecked exceptions and Error instances trigger rollback by default; checked exceptions do not. rollbackFor can change that rule.

  • The default isolation setting delegates to the underlying transaction provider. The default timeout comes from the transaction manager, and read-only mode defaults to false.

  • Self-invocation bypasses the proxy, so an annotated method called directly within the same object does not receive transactional interception.

  • JdbcTemplate translates JDBC failures, while Spring Data repository exception translation converts persistence failures into Spring’s DataAccessException hierarchy.

Practical Boundaries and Best Practices

  • Use constructor injection for persistence components and keep repository interfaces focused on persistence concerns.

  • Always use parameterized queries for untrusted values to prevent SQL injection.

  • Keep credentials outside source control, use encrypted database connections, and apply least-privilege accounts.

  • In multi-database applications, qualify each DataSource, repository, and transaction manager explicitly rather than relying on ambiguous auto-configuration.

  • Size connection pools according to database capacity and expected concurrency instead of increasing them without measurement.

  • Use Spring Boot Actuator and Micrometer instrumentation to monitor pool saturation, transaction outcomes, and database performance.

23

How does Spring Boot use an embedded web server to run an application without requiring a separately deployed servlet container?

An embedded server is a web server bundled inside the Spring Boot application and started automatically as part of the application process. Instead of deploying a WAR file to an external Tomcat, Jetty, or similar container, the application can run as a self-contained executable JAR.

Read full answer

Applies toSpring Boot 3.x with Spring Framework 6.x on Java 17+

How It Works

Spring Boot packages a servlet container or reactive server with the application. When the main application starts, Spring Boot:

  1. Detects the application’s web application type.

  2. Configures the appropriate embedded server.

  3. Registers the application’s controllers and components with it.

  4. Starts the server on the configured port.

  5. Keeps the server running until the application shuts down.

A typical Spring Boot application can then be launched directly from its executable JAR. There is no need to copy the JAR into an external server’s deployment directory.

Servlet and Reactive Models

Spring Boot supports two main embedded-server execution models:

Model Server components Programming model
Servlet Embedded Tomcat, Jetty, or Undertow Spring MVC with HttpServletRequest and HttpServletResponse
Reactive Embedded Reactor Netty, Tomcat, Jetty, or Undertow Spring WebFlux with reactive types such as Mono and Flux

The application type is determined from the dependencies on the classpath and the application’s configuration. If Spring MVC is present, Spring Boot generally creates a servlet web application; if Spring WebFlux is present and Spring MVC is not, it creates a reactive web application. The type can also be forced through configuration.

Server Configuration

The embedded server normally listens on port 8080. This can be changed without modifying application code, for example by setting server.port=8081 in application.properties or the equivalent YAML configuration. Graceful shutdown can be enabled with server.shutdown=graceful.

Other common settings include the context path, error handling, HTTP/2, SSL, and connection limits. These settings are implementation-independent where Spring Boot provides standardized configuration properties. Servlet and reactive applications use the same server.* namespace for shared settings, although some properties apply only to one model.

Packaging and Deployment

An embedded server does not mean the application is limited to local development. A Spring Boot application is commonly packaged as an executable JAR and deployed to:

  • Virtual machines

  • Kubernetes clusters

  • Cloud platforms

  • Container images

  • Process supervisors such as systemd

The JAR contains the application code, dependencies, and embedded server. The deployment platform starts the JVM, while the application manages its own HTTP listener. An executable JAR is built with Spring Boot’s Maven or Gradle plugin.

Advantages and Limitations

  • Simple deployment: The application and server travel together.

  • Consistent packaging: Developers and operations use the same artifact.

  • Less infrastructure: A separate application-server installation is unnecessary for many workloads.

  • Fast startup: The server begins without an external container lifecycle.

  • Scaling responsibility: A single JVM or instance has finite CPU, memory, and connection capacity, so horizontal scaling may still be required.

  • Operational isolation: Running each application in its own JVM provides process isolation; shared external containers instead centralize server configuration and may offer specialized traffic management.

  • Production tuning: Embedded servers still require appropriate monitoring, TLS termination, graceful shutdown, thread and connection management, and capacity planning.

Best Practices and Common Pitfalls

  • Use executable JARs for containerized deployments: Package the application with Spring Boot’s build tooling rather than manually assembling dependencies.

  • Do not depend on an external container implicitly: If production requires deployment to a separate server, package a WAR and deploy it with the compatible container instead.

  • Configure graceful shutdown: Spring Boot supports graceful shutdown so that in-flight requests can complete before the process exits.

  • Keep secrets outside source control: Use environment variables, mounted secrets, or a secrets manager rather than embedding credentials in configuration files.

  • Expose health information appropriately: Spring Boot Actuator provides health and metrics endpoints, but sensitive endpoints should not be exposed publicly without authentication and network controls.

  • Match the web model to the workload: Use Spring MVC for traditional request-response applications and Spring WebFlux when non-blocking, high-concurrency behavior is genuinely required.

24

What responsibilities does the SpringApplication class have when starting a Spring Boot application?

SpringApplication is Spring Boot’s central bootstrap class: it prepares the environment, deduces the application type, creates and refreshes the ApplicationContext, registers primary configuration sources, coordinates startup events and runners, and—for web applications—lets the embedded server start as part of context refresh. It returns the running ConfigurableApplicationContext; it does not itself keep the JVM alive.

Read full answer

Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17+

Core Responsibilities

  • Environment preparation: Creates the Environment and loads property sources such as application.properties, application.yml, environment variables, system properties, and command-line arguments.

  • Application-type detection: Deduces the WebApplicationType (SERVLET, REACTIVE, or NONE) from the classpath; it can be overridden with setWebApplicationType.

  • Context creation: Creates the matching ApplicationContext, such as an annotation-based servlet or reactive web context, or a plain annotation context.

  • Primary source registration: Registers the primary configuration sources, usually the @SpringBootApplication class. The context then processes @ComponentScan and @EnableAutoConfiguration from that source.

  • Context refresh: Refreshes the context, creating singleton beans, injecting dependencies, and—for web applications—starting the embedded server. Auto-configuration participates here when @EnableAutoConfiguration is present, selecting configurations from the classpath and existing beans.

  • Lifecycle coordination: Publishes startup events, calls ApplicationRunner and CommandLineRunner beans after refresh, and publishes ApplicationReadyEvent after the runners complete.

Startup Process

SpringApplication.run(...) performs a sequence similar to this:

  1. Create a SpringApplication instance; its constructor deduces the default application type and records the primary sources.

  2. Prepare the environment, including configuration data and command-line arguments.

  3. Create the ApplicationContext for the deduced type.

  4. Prepare the context: apply initializers and listeners and load the primary sources.

  5. Refresh the context: create singleton beans, inject dependencies, and start the embedded server for a web application.

  6. Publish ApplicationStartedEvent, then call ApplicationRunner and CommandLineRunner beans.

  7. Publish ApplicationReadyEvent and return the ConfigurableApplicationContext.

For a web application, the embedded server’s non-daemon threads keep the JVM running until a shutdown signal. A non-web application may exit once run returns if no non-daemon threads remain.

How It Differs from Related Components

Component Responsibility
SpringApplication Bootstraps the application and coordinates startup.
@SpringBootApplication Marks the primary configuration class and combines configuration, component scanning, and auto-configuration behavior.
ApplicationContext Holds and manages beans, dependencies, application events, and infrastructure components.
Embedded web server Hosts the application and accepts HTTP requests after startup.

SpringApplication does not replace ApplicationContext; it creates and initializes it. Likewise, @SpringBootApplication does not perform startup itself—it supplies configuration that the context processes.

Best Practices and Common Pitfalls

  • Prefer SpringApplication.run(...) for ordinary applications. Construct SpringApplication directly only when startup customization is required.

  • Keep secrets and environment-specific values outside source code; use environment variables or an external configuration provider.

  • Use constructor injection for application dependencies.

  • Use ApplicationRunner or CommandLineRunner for post-startup validation or initialization, rather than performing essential work in main.

  • Do not call close() immediately after run in a long-running server; doing so stops the application.

  • Do not confuse SpringApplication with ApplicationContext: the former orchestrates startup, while the latter remains the bean container.

25

What exactly is Spring Security, and what makes it important for a Spring application?

Spring Security is the official Spring project for authentication, authorization, and application-level security. It integrates with Spring applications through a filter chain and method-security APIs, helping protect credentials, sessions, browser requests, and access to application functionality.

Read full answer

Applies toSpring Security 7.0 with Spring Boot 4.0 and Spring Framework 7.0 on Java 17+

How It Works: The Filter Chain

Spring Security does not wrap controller classes. Instead, it registers a FilterChainProxy, exposed to the servlet container as the springSecurityFilterChain filter, which runs before the dispatcher servlet.

In Spring Security 7.0, the chain is configured through one or more SecurityFilterChain beans. Important filters include:

  • SecurityContextHolderFilter: retrieves the current SecurityContext from the configured SecurityContextRepository; the default repository is backed by the HTTP session.

  • UsernamePasswordAuthenticationFilter: processes form-login requests when form login is configured.

  • BasicAuthenticationFilter: processes HTTP Basic authentication when HTTP Basic is configured.

  • ExceptionTranslationFilter: handles authentication and access-denied exceptions, producing an appropriate error response or invoking the authentication entry point.

  • AuthorizationFilter: evaluates the request authorization rules and is normally the final authorization filter in the chain.

Spring Boot supplies a default security configuration when the application does not provide its own. Under the relevant auto-configuration conditions, it also creates an in-memory user with a generated password, which is why Boot can log Using generated security password. Spring Security can generate a default login page when form login is enabled and no custom page is provided, but @EnableWebSecurity or an empty custom chain does not by itself provide Boot’s generated user.

Core Building Blocks

  • Authentication: Represents the caller, including its principal, credentials state, and granted authorities.

  • AuthenticationManager: Coordinates AuthenticationProvider implementations. A successful provider returns an authenticated Authentication; failure normally results in an AuthenticationException.

  • UserDetailsService: Loads user information and authorities, commonly from a database or another identity store.

  • PasswordEncoder: Encodes passwords and verifies submitted passwords against encoded values. It does not persist the encoded password itself.

  • PasswordEncoderFactories.createDelegatingPasswordEncoder(): Creates the recommended delegating encoder, which uses bcrypt by default and records the encoder identifier in a prefix such as {bcrypt}.

  • SecurityContextHolder: Holds the current security context, using a thread-local strategy by default.

  • GrantedAuthority: Represents a role or permission, commonly using names such as ROLE_ADMIN; these are evaluated by request rules and method-security annotations.

Why It Matters in Practice

  • Secure defaults: The framework enables protections such as CSRF validation, session-fixation protection, security headers, and cache-control headers by default.

  • HTTPS-aware headers: HSTS is added to responses for HTTPS requests. Other defaults include X-Content-Type-Options: nosniff and headers that prevent sensitive responses from being cached.

  • Maintained cryptography: PasswordEncoder implementations include bcrypt, PBKDF2, and Argon2, avoiding custom password-hashing code.

  • Layered authorization: URL authorization and method security can complement each other, although every layer must be configured correctly.

  • Consistent integration: The framework supports servlet and reactive applications, as well as mechanisms such as form login, HTTP Basic, OAuth 2.0 and OpenID Connect, SAML, X.509 authentication, and remember-me.

  • Centralized review: A declarative filter chain and method-security configuration make security rules easier to review and maintain than scattered checks in controllers.

Security concern Spring Security mechanism
Who is the caller? Authentication, authentication filters, OAuth 2.0/OIDC, SAML, X.509
What may the caller access? authorizeHttpRequests, @PreAuthorize
How are passwords protected? PasswordEncoder
How are browser requests protected? CSRF protection, session protection, and security headers

Best Practices and Common Pitfalls

  • Use authorizeHttpRequests; authorizeRequests was deprecated in Spring Security 6.x and removed in 7.0. WebSecurityConfigurerAdapter was removed in 6.0.

  • Use @EnableMethodSecurity; @EnableGlobalMethodSecurity was removed in Spring Security 7.0.

  • Use constructor injection for application collaborators instead of field injection with @Autowired.

  • Keep CSRF enabled when a browser can automatically attach credentials, such as cookies or HTTP-authentication credentials. Disabling CSRF is reasonable for a client that sends a bearer token in the Authorization header because browsers do not attach that header automatically.

  • For browser SPAs, use the current SPA CSRF support, including SpaCsrfTokenRequestHandler, arrange safe token delivery to JavaScript, and send the token in the expected request header.

  • Always encode passwords with a PasswordEncoder; never store plaintext or compare passwords with equals.

  • Enforce authorization on the server. Hidden or disabled UI elements improve usability but are not security controls.

26

What role does Spring play in designing and implementing a microservices architecture?

Spring Boot provides the foundation for building independently deployable services, while Spring Framework supplies dependency injection, web APIs, validation, data access, and transaction management. Spring Cloud adds distributed capabilities such as service discovery, centralized configuration, API gateways, circuit breakers, and distributed tracing.

Read full answer

Applies toSpring Boot 3.5, Spring Framework 6.2, Spring Cloud 2025.0, Java 17+

How Spring Supports Microservices

Each microservice can be a separate Spring Boot application with its own codebase, deployment lifecycle, and preferably its own data store.

  • Dependency injection: Constructor injection makes components testable and avoids hidden dependencies.

  • REST APIs: Spring MVC and RestController expose HTTP endpoints; validation and ProblemDetail support consistent error handling.

  • Configuration: Spring Boot’s externalized configuration supports environment-specific settings. Spring Cloud Config can centralize configuration when required.

  • Operations: Spring Boot Actuator provides health, metrics, and information endpoints. Kubernetes readiness and liveness probes can use its health endpoint.

  • Data access: Spring Data JDBC, JPA, and R2DBC provide repository abstractions, while @Transactional manages local database transactions.

  • Messaging: Spring for RabbitMQ and Spring Kafka support asynchronous, event-driven workflows.

Spring Cloud Capabilities

Spring Cloud is optional; it is useful when services must coordinate across the platform.

  • Service discovery: DiscoveryClient and client-side load balancing let one service locate another without hard-coding every instance.

  • API gateway: Spring Cloud Gateway provides routing, filters, authentication integration, and optional request-rate limiting at the platform edge. In Spring Cloud 2025.0, use spring-cloud-starter-gateway-server-webmvc for a servlet application or spring-cloud-starter-gateway-server-webflux for a reactive application.

  • Circuit breaker: spring-cloud-starter-circuitbreaker-resilience4j isolates failures and supports fallback behavior.

  • Configuration management: The Spring Cloud Config server and client provide centralized, versioned configuration.

  • Tracing: Micrometer Tracing with the OpenTelemetry bridge and an OTLP exporter propagates traces across service boundaries.

For outbound HTTP calls, use RestClient for synchronous requests or WebClient for non-blocking and streaming requests. RestTemplate remains available but is in maintenance mode.

Designing Reliable Service Boundaries

A Spring-based microservice should expose a narrow business capability rather than a layer of a shared application. Each service should own its schema and avoid reaching directly into another service’s database.

Use timeouts, bounded retries, circuit breakers, and correlation IDs for remote calls. Idempotency keys or deduplication are important for message consumers and retryable operations. Prefer the transactional outbox pattern over distributed two-phase transactions when coordinating database updates and event publication.

Best Practices and Common Pitfalls

  • Use constructor injection rather than field injection with @Autowired.

  • Keep services cohesive; avoid creating a “microservice” that is simply a distributed shared library.

  • Apply API authentication and authorization consistently at the gateway and service layers.

  • Restrict sensitive Actuator endpoints and use readiness probes that verify only required dependencies.

  • Set explicit connection, request, and retry timeouts; excessive retries can amplify an outage.

  • Keep database transactions local and design cross-service workflows for partial failure.

  • Version external APIs and maintain backward-compatible contracts.

  • Use Spring Cloud only when distributed coordination is needed; a standalone service does not require it.

27

In Spring, what role does Spring Cloud Gateway play in a distributed application architecture?

Spring Cloud Gateway is a reactive API gateway that routes external requests to internal services while adding cross-cutting functionality such as authentication, transformations, rate limiting, monitoring, and resilience. It provides a single entry point for clients and keeps service-specific concerns outside individual microservices.

Read full answer

Applies toSpring Cloud Gateway 4.3.x in the Spring Cloud 2025.0.x release train, with Spring Boot 4.0.x and Spring Framework 7.0.x on Java 17+

What It Provides

Spring Cloud Gateway sits between clients and backend services:

  • Routing: Selects a destination service using request predicates such as paths, methods, headers, or query parameters.

  • Request filtering: Adds or removes headers, rewrites paths, validates tokens, or restricts access before forwarding a request.

  • Response filtering: Adds response headers or transforms response content.

  • Resilience: Integrates with circuit breakers, retry handling, and request timeouts.

  • Observability: Exposes metrics and routing information through Spring Boot Actuator and Micrometer.

  • Service discovery: Can locate service instances through Spring Cloud LoadBalancer and a service registry such as Eureka or Consul.

It is intended primarily for edge routing and API concerns, not for hosting normal business logic.

How It Works

Spring Cloud Gateway is based on Spring WebFlux and Reactor Netty, so request processing is asynchronous and non-blocking. A typical request follows this path:

  1. The client sends an HTTPS request to the gateway.

  2. A route predicate determines whether the request matches a configured route.

  3. Matching filters process the request.

  4. The gateway forwards the request to the selected service instance.

  5. Response filters process the result before it is returned to the client.

A route conceptually contains four parts:

  • ID: A unique route identifier.

  • URI: The destination service or external endpoint.

  • Predicates: Conditions that must be satisfied for the route to match.

  • Filters: Components that modify the request or response.

Routes can be declared through configuration or constructed programmatically with RouteLocatorBuilder. Programmatic definitions are useful when routes depend on application configuration or custom business rules.

Comparing Its Role

Component Primary responsibility Typical location
Spring Cloud Gateway HTTP routing and API-edge policies Between clients and services
Service mesh Low-level service-to-service networking, traffic policy, and telemetry Beside each service
API management platform Productized API catalog, developer portal, quotas, and analytics Public API-management edge
Spring Boot microservice Business or data-processing logic Internal service network

These components can coexist. For example, a gateway may expose public APIs while a service mesh handles internal service-to-service communication.

When to Use It

Spring Cloud Gateway is a good fit when an application needs:

  • A stable public URL for several backend services.

  • Path-based routing such as /orders/** to an orders service.

  • Centralized security enforcement or token validation.

  • Consistent cross-cutting policies across APIs.

  • Gateway-level metrics, tracing, rate limiting, or resilience.

  • Integration with Spring Cloud service discovery.

It is less appropriate when only one backend service exists, or when routing and policy complexity would be better handled inside a full API-management platform.

Best Practices and Limitations

  • Keep the gateway thin: Place business decisions in domain services. Large gateway route handlers can become another monolith.

  • Avoid blocking operations: Blocking calls in a reactive handler can reduce gateway throughput. Use reactive clients such as WebClient for downstream HTTP calls.

  • Configure timeouts and bounded retries: Retry only operations that are safe to repeat, and avoid aggressive retry policies that amplify outages.

  • Secure the edge: Terminate TLS where required, validate tokens against trusted identity providers, and strip spoofable internal headers.

  • Use circuit breakers for unstable dependencies: A failing downstream service should not exhaust all gateway resources.

  • Observe routes individually: Track latency, errors, status codes, and traffic volume for each route.

  • Use the reactive deployment model: Spring Cloud Gateway uses Reactor Netty rather than deployment as a traditional servlet WAR.

28

Could you explain what reactive programming is and describe how Spring supports it?

Reactive Programming represents data as streams of events or values that can be processed asynchronously, composed with operators, and cancelled when no longer needed. Spring supports this model primarily through Spring WebFlux, Project Reactor, WebClient, and reactive database access with Spring Data R2DBC.

Read full answer

Applies toSpring Boot 3.5.x with Spring Framework 6.2.x, Project Reactor 3.7.x, and Java 17+

How It Works

Reactive applications commonly use the Reactive Streams model:

  • Publisher: Produces zero, one, or many values and signals completion or an error.

  • Subscriber: Requests and receives those values.

  • Operators: Transform, filter, combine, throttle, retry, or otherwise process the stream.

  • Backpressure: Allows a subscriber to communicate how much data it can process. Not every data source provides true backpressure, so the underlying source must be checked.

  • Cancellation and timeouts: Propagate downstream signals so unnecessary work and resources can be stopped.

Project Reactor provides the Mono<T> and Flux<T> types commonly used with Spring. A Mono represents zero or one result, while a Flux represents zero to many results. Both are lazy: work generally begins when the result is subscribed to.

Spring’s Reactive Support

  • Spring WebFlux: Implements non-blocking HTTP servers and clients using the Reactive Streams model. It is the main choice for end-to-end non-blocking applications.

  • WebClient: A reactive HTTP client that supports streaming responses, backpressure-oriented processing, timeouts, and cancellation. For traditional blocking integrations, Spring also provides RestClient.

  • Spring Data R2DBC: Provides reactive, non-blocking access to relational databases. It complements JDBC rather than replacing it in blocking applications.

  • Spring MVC reactive return types: MVC controllers can return Mono or Flux and adapt them to the servlet response model. However, this does not make the entire servlet request stack non-blocking.

  • Reactive messaging: Spring Integration and Spring AMQP can expose message flows and channels through reactive APIs.

Choosing Between WebFlux and MVC

Concern WebFlux Spring MVC
Execution model Primarily non-blocking and event-loop based Servlet-based, with blocking and asynchronous options
Typical database access R2DBC JDBC or R2DBC
HTTP client WebClient RestClient
Reactive return values Native support Supported through adaptation
Best fit High-concurrency streaming and I/O-bound systems Existing servlet applications and simpler blocking workloads

Use WebFlux when most downstream operations are genuinely non-blocking. Use MVC when the application already depends heavily on servlet APIs, JDBC libraries, or other blocking components.

Best Practices and Pitfalls

  • Avoid calling .block() inside a WebFlux handler, operator, or repository method. Reserve blocking calls for an intentional boundary outside the reactive chain.

  • Do not perform JDBC, filesystem, or other blocking work on a Reactor event-loop thread. If blocking is unavoidable, isolate it with Schedulers.boundedElastic() and size that scheduler appropriately.

  • Keep the chain non-blocking end to end; using WebClient alone does not help if later stages perform blocking I/O.

  • Propagate cancellation, configure meaningful connection and response timeouts, and avoid unbounded stream creation.

  • Use R2DBC instead of JDBC when the surrounding application is designed around non-blocking database access.

  • Test reactive behavior and ordinary application concerns, including error handling, retries, timeout paths, cancellation, and the backpressure behavior of each data source.

29

What role does Spring WebFlux play in the reactive web stack introduced by Spring Framework 5?

Spring WebFlux is Spring Framework 5’s reactive web programming model, built around non-blocking I/O and the Reactive Streams specification. It enables an application to handle many concurrent requests efficiently by using event-loop threads rather than dedicating a thread to each request.

Read full answer

Applies toSpring Framework 5.3 (Spring 5); current Spring Framework 6.2 on Java 17+

Position in Spring 5

Spring Framework 5.0 introduced WebFlux as the counterpart to the established Servlet-based Spring MVC framework. It supports applications that perform most I/O asynchronously, such as calling remote services, streaming data, or communicating with reactive databases.

The reactive stack primarily consists of:

  • Spring WebFlux: The web framework and infrastructure for reactive HTTP servers and clients.

  • Reactor Core: Provides the Mono and Flux publisher types and reactive operators.

  • Reactive Streams: The standard interface used by compatible reactive components.

  • Spring Security reactive support: Provides non-blocking authentication and authorization support.

How It Works

A WebFlux controller can return a Mono<T> or Flux<T>. The framework subscribes to the returned publisher and writes its emissions to the HTTP response.

  • Mono<T>: Represents zero or one result.

  • Flux<T>: Represents zero, one, or many results.

  • Operators: Transform, combine, filter, and control reactive streams.

  • Backpressure: Lets a consumer indicate how much data it can process, although support depends on the specific data source.

Because the request-processing pipeline does not normally occupy one thread per connection, WebFlux can scale well for high-concurrency, I/O-bound workloads. It does not automatically make an application faster; CPU-heavy processing and blocking operations can still limit throughput.

Main Programming Models

WebFlux supports two commonly used styles:

  • Annotated controllers: Use @RestController, @GetMapping, and other Spring MVC annotations, but return reactive publisher types.

  • Functional routing: Uses RouterFunctions and HandlerFunctions to define routes and handlers as Java functions.

WebFlux also includes WebClient, Spring’s non-blocking HTTP client. It can exchange data with MVC or WebFlux applications and supports streaming responses, unlike the primarily blocking RestTemplate. RestTemplate remains available but is in maintenance mode.

WebFlux Compared with Spring MVC

Aspect Spring WebFlux Spring MVC
Programming model Reactive Streams Servlet-based
Request execution Event-loop and non-blocking by default Usually one request thread per request
Typical return type Mono<T> or Flux<T> Regular object or DeferredResult
Database integration R2DBC and other reactive libraries JDBC, JPA, and other blocking libraries
HTTP client WebClient WebClient or maintenance-mode RestTemplate
Best suited for Reactive and high-concurrency I/O Blocking stacks and traditional servlet applications

The two frameworks can coexist in one application, and WebClient provides a convenient bridge between them.

Version and Namespace Considerations

Spring Framework 5.3 was the final Spring 5 release, but it has reached end of life for open-source support. New production systems should use Spring Framework 6.2 with Java 17 or later. Spring Framework 6 and later use jakarta.* namespaces, whereas Spring 5 applications use the older javax.* namespaces.

WebFlux itself continues to be supported in current Spring releases; the major migration concerns are the Jakarta namespace change, Java baseline, dependency updates, and removal of obsolete APIs.

Adoption Guidance and Pitfalls

  • Use WebFlux for reactive I/O: It is particularly suitable for streaming APIs, gateways, and services dominated by remote calls or reactive database access.

  • Avoid blocking calls on event-loop threads: Operations such as JPA, JDBC, Thread.sleep, or synchronous file I/O can stall the event loop. Use R2DBC or isolate unavoidable blocking work with a bounded Scheduler.

  • Choose MVC for blocking dependencies: A conventional application built around JPA, JDBC, or synchronous libraries is usually simpler and more reliable with Spring MVC.

  • Do not subscribe manually in controllers: Return the publisher and let the WebFlux infrastructure manage its lifecycle.

  • Plan for resource limits: Reactive concurrency still requires sensible timeouts, connection-pool limits, cancellation handling, and backpressure where supported.

30

What is back-pressure in Reactive Streams, and how is it used to coordinate demand between publishers and subscribers?

Back-pressure is a demand-driven flow-control mechanism that prevents a publisher from producing data faster than a subscriber can consume it. In Reactive Streams, the subscriber communicates its capacity through demand signals, allowing the publisher to slow down, buffer, or handle excess data safely.

Read full answer

Applies toSpring Framework 6.2 with Spring Boot 3.5 and Project Reactor 3.7 on Java 17+

How It Works

Reactive Streams defines a contract between three main participants:

  • Publisher: Produces data, such as database rows or HTTP response elements.

  • Subscriber: Requests and processes that data.

  • Subscription: Represents the connection through which the subscriber sends demand.

After subscribing, the subscriber calls request(n) on the Subscription, indicating that it is prepared to receive up to n additional elements. A compliant publisher must not emit more elements than the outstanding demand allows, although it may send terminal signals such as onComplete or onError without additional demand.

Demand generally propagates upstream:

  1. The subscriber requests a number of elements.

  2. The subscription records that demand.

  3. The publisher receives or derives the request.

  4. The publisher emits only the permitted number of elements.

  5. Processing one element may cause the subscriber to request another, creating a continuous feedback loop.

This mechanism allows a fast source—such as a high-throughput database query or network stream—to cooperate with a slower processor without creating an unbounded queue.

Why It Matters

Without back-pressure, a mismatch between production and consumption rates can lead to:

  • Excessive memory consumption from unbounded buffering

  • Increased garbage-collection pressure

  • Request or connection timeouts

  • Application instability under load

  • Unnecessary work for items that are no longer needed

Back-pressure is therefore not merely a performance optimization. It is an important mechanism for maintaining predictable resource usage and system stability.

Handling Excess Data

The Reactive Streams specification defines how demand is communicated, but it does not prescribe one universal overflow strategy. Project Reactor provides operators for common policies:

Strategy Reactor operator Behavior
Buffer excess elements onBackpressureBuffer Stores elements until the subscriber can consume them
Drop incoming elements onBackpressureDrop Discards elements when downstream demand is unavailable
Keep the latest element onBackpressureLatest Replaces the buffered value with the newest one
Fail the stream onBackpressureError Signals an error when the downstream cannot accept data

A bounded buffer is generally safer than an unbounded one because it places an explicit limit on memory use. However, when the buffer reaches its capacity, the selected overflow behavior must match the business requirements.

Back-Pressure in Spring

In Spring’s reactive stack, back-pressure works through the Project Reactor implementation of Reactive Streams:

  • Spring WebFlux builds on Reactive Streams and Reactor.

  • WebClient supports non-blocking HTTP exchanges and reactive response processing.

  • Reactor Netty provides the underlying reactive network I/O used by WebFlux and WebClient.

  • R2DBC exposes database results through reactive publishers, with demand propagated to the driver where the driver supports it.

RestClient is synchronous and does not provide Reactive Streams back-pressure. Spring MVC is likewise primarily blocking and is not organized around subscriber-driven demand.

Practical Guidance

  • Request data only when downstream processing capacity is available.

  • Use bounded buffers and choose an overflow strategy explicitly.

  • Apply back-pressure operators close to the point where demand cannot be propagated naturally.

  • Avoid performing blocking work inside map, flatMap, or other reactive operators.

  • Remember that operators such as publishOn and flatMap can affect demand propagation and buffering behavior.

  • Treat back-pressure differently from rate limiting: back-pressure is consumer-driven flow control, while rate limiting restricts a producer independently of downstream demand.

31

How should unit tests be written and executed in a Spring application?

Unit testing in Spring means testing an individual class in isolation, usually with JUnit Jupiter and Mockito, without starting the Spring application context. Testing with a real application context, database, or HTTP server is an integration-testing concern.

Read full answer

Applies toSpring Boot 4.0 and Spring Framework 7.0 on Java 17+, using JUnit Jupiter 6 and Mockito 5

How Unit Testing Works

A conventional unit test follows the arrange-act-assert pattern:

  1. Create the object under test.

  2. Replace external dependencies with mocks or test doubles.

  3. Configure only the behavior relevant to the scenario.

  4. Invoke the method under test.

  5. Assert the observable result and verify important interactions.

Spring does not need to create the object under test. Constructor injection makes unit testing straightforward because dependencies can be supplied directly to the constructor without field injection or an application context.

Test Dependencies

  • JUnit Jupiter: Provides @Test, test lifecycle callbacks, and assertions such as assertEquals and assertThrows.

  • Mockito: Creates mocks, configures return values, and verifies interactions.

  • AssertJ: Provides fluent assertions such as assertThat(result).isEqualTo(expected).

  • Spring Boot test starter: Supplies the commonly used testing dependencies with versions managed by the Spring Boot BOM.

For Maven, add:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Unit Tests and Spring Tests

Test type Context started Dependencies Appropriate use
Plain JUnit and Mockito test No Collaborators replaced with mocks Business logic and isolated class behavior
@WebMvcTest Limited MVC context Typically a mocked service collaborator Request mapping, validation, and serialization
@DataJpaTest Limited persistence context Embedded database by default JPA mappings and repository queries
@SpringBootTest Full application context Configured application infrastructure Component and infrastructure integration

Use plain unit tests for most domain and service logic because they run faster and provide precise diagnostics. Use Spring test slices when framework behavior itself needs verification.

Code Example

This test does not start Spring or access a database or network:

package com.example.orders;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private OrderRepository orderRepository;

    private OrderService orderService;

    @BeforeEach
    void setUp() {
        orderService = new OrderService(orderRepository);
    }

    @Test
    void shouldCreateOrderForAnExistingCustomer() {
        when(orderRepository.findCustomerById(42L))
                .thenReturn(Optional.of(new Customer(42L, "Avery")));

        Order order = orderService.createOrder(42L, "Laptop stand");

        assertThat(order.customerId()).isEqualTo(42L);
        assertThat(order.description()).isEqualTo("Laptop stand");
        verify(orderRepository).save(any(Order.class));
    }

    @Test
    void shouldRejectAnOrderForAnUnknownCustomer() {
        when(orderRepository.findCustomerById(999L))
                .thenReturn(Optional.empty());

        assertThrows(
                CustomerNotFoundException.class,
                () -> orderService.createOrder(999L, "Laptop stand")
        );

        verify(orderRepository, never()).save(any(Order.class));
    }
}

The corresponding package-private production types can be:

package com.example.orders;

import java.util.Optional;

import org.springframework.stereotype.Repository;

@Repository
class OrderRepository {

    Optional<Customer> findCustomerById(long customerId) {
        // A real implementation would query persistent storage.
        return Optional.empty();
    }

    Order save(Order order) {
        return order;
    }
}

class OrderService {

    private final OrderRepository orderRepository;

    OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    Order createOrder(long customerId, String description) {
        if (orderRepository.findCustomerById(customerId).isEmpty()) {
            throw new CustomerNotFoundException(customerId);
        }

        Order order = new Order(customerId, description);
        orderRepository.save(order);
        return order;
    }
}

record Customer(long id, String name) {
}

record Order(long customerId, String description) {
}

class CustomerNotFoundException extends RuntimeException {
    CustomerNotFoundException(long customerId) {
        super("Customer not found: " + customerId);
    }
}

Best Practices and Execution

  • Test observable behavior: Prefer assertions on returned values, state, or exceptions over excessive interaction verification.

  • Avoid over-mocking: Do not mock the class under test or simple value objects. Mock boundaries such as repositories, gateways, and publishers.

  • Keep tests isolated: Each test should create its own object graph and remain independent of execution order.

  • Avoid unnecessary contexts: Do not use @SpringBootTest for conventional unit tests.

  • Use deterministic data: Avoid dependence on the system clock, random values, network access, or shared mutable fixtures.

  • Use current Spring mock annotations: When a Spring-managed mock is required, use Spring Framework’s @MockitoBean. The older @MockBean is deprecated in Spring Framework 6.2 and later.

  • Execute from an IDE or wrapper: On Linux and macOS, use ./mvnw test for Maven or ./gradlew test for Gradle. On Windows, use .\mvnw.cmd test or .\gradlew.bat test.

32

How does the Spring TestContext Framework support the execution of tests?

The Spring TestContext Framework provides managed test execution by preparing an appropriate Spring ApplicationContext, injecting dependencies into test classes, integrating transactions, and notifying listeners when tests run. It works closely with JUnit Jupiter and Spring’s test annotations to give tests the benefits of Spring’s dependency injection and configuration without manually creating or closing containers.

Read full answer

Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17+

How It Works

The framework is integrated into JUnit through SpringExtension. When a test uses an annotation such as @SpringJUnitConfig, @SpringBootTest, or a Spring Boot test slice, the extension prepares the required application context before the test runs.

The resulting context uses the standard ApplicationContext lifecycle and dependency-injection model. Test classes can therefore receive components, repositories, configuration properties, and other beans through constructor injection or other supported injection mechanisms.

The framework also controls the context’s lifecycle. Contexts are cached and reused across compatible tests, which reduces repeated startup costs. Tests that modify shared context state can request that their context be refreshed or removed from the cache with @DirtiesContext.

Main Capabilities

  • ApplicationContext management: Creates, configures, caches, and closes test application contexts.

  • Dependency injection: Injects application beans into test classes, test configurations, and test components.

  • Transaction integration: Supports transactional behavior and automatic rollback when a test transaction is managed by Spring.

  • Test execution events: Publishes events before and after test-class and test-method execution through TestExecutionListener.

  • JUnit integration: Uses SpringExtension to connect the TestContext Framework with JUnit Jupiter.

  • Context customization: Allows tests to load custom configurations, profiles, property sources, and context initializers.

  • Parallel-test support: Can participate in parallel execution when configured, with isolation considerations handled by Spring’s context management.

Context Selection

Different annotations determine how much of the application is loaded.

Test Style Context Behavior
@SpringJUnitConfig Loads a lightweight context using the classes named in @ContextConfiguration.
@SpringBootTest Loads a complete application context. It uses a mock web environment by default; an embedded server starts with webEnvironment = RANDOM_PORT or DEFINED_PORT.
@WebMvcTest Loads a web-layer slice containing MVC components rather than the entire application.
@DataJpaTest Configures a persistence-focused slice with an embedded database by default.
@JsonTest Configures JSON serialization and deserialization components.
@RestClientTest Configures supported HTTP client components and mock servers.

These slices reduce test startup time, but they are not substitutes for integration tests that verify how multiple application layers interact.

Transactions and Listeners

Spring can manage transactions for integration tests by applying @Transactional to the test class or method. Test code then executes inside a test-managed transaction that is rolled back after the test by default, helping isolate database changes. This requires a compatible PlatformTransactionManager; embedded databases are supported as well as external databases. @Rollback(false) or @Commit changes the default rollback behavior, so explicit commit-semantics tests remain necessary.

The TestExecutionListener API allows frameworks and infrastructure code to react to test lifecycle events. Spring Framework supplies built-in listeners, and Spring Security test support uses this mechanism. Custom listeners can also be registered when a project needs additional setup or cleanup around test execution.

Best Practices

  • Prefer constructor injection in tests, matching production-code practices.

  • Use the narrowest suitable context or slice for unit-level component tests, and reserve @SpringBootTest for broader integration coverage.

  • Avoid relying on test execution order or mutating cached context state without restoring it.

  • Use @DirtiesContext only when necessary because closing and rebuilding a context is relatively expensive.

  • Keep database tests transactional when rollback provides adequate isolation; use explicit cleanup or dedicated databases when commit semantics must be tested.

  • Treat the TestContext Framework as Spring’s integration layer, while JUnit Jupiter remains responsible for the overall test-discovery and execution model.

33

What role does the `@SpringBootTest` annotation play when creating integration tests in Spring Boot?

@SpringBootTest is the primary annotation for integration testing in Spring Boot. It loads a Spring ApplicationContext using the application’s auto-configuration and bean definitions, allowing tests to exercise real components and their dependencies.

Read full answer

Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17+

How It Works

When a test class is annotated with @SpringBootTest, Spring Boot:

  1. Uses the classes specified by the annotation’s classes attribute, if provided; otherwise, it searches for the application’s @SpringBootConfiguration class.

  2. Creates an ApplicationContext containing the application’s beans and imported configuration.

  3. Applies Spring Boot auto-configuration and supports configuration properties and test-specific property sources.

  4. Makes the resulting beans available for injection into the test.

  5. Caches the context so multiple test methods can reuse it when their configurations are compatible.

For a web-based application, the default webEnvironment is MOCK. This creates a mock servlet or reactive web environment without opening a network port. A non-web application instead receives a non-web application context.

Test Environment Options

The webEnvironment attribute controls how the application is started:

  • MOCK: Creates a mock servlet or reactive web environment for a web application without starting an embedded server. This is the default value.

  • RANDOM_PORT: Starts an embedded server on an available random port.

  • DEFINED_PORT: Starts the embedded server using the configured port, commonly server.port.

  • NONE: Uses a non-web application context.

A test that sends real HTTP requests can therefore use webEnvironment = RANDOM_PORT together with a dependency such as spring-boot-starter-web.

When to Use It

Use @SpringBootTest when the test needs to verify interactions across multiple application layers, such as:

  • Controller-to-service-to-repository behavior

  • Auto-configuration of infrastructure components

  • Bean dependency wiring and initialization

  • Database integration through a real repository

  • End-to-end HTTP behavior with an embedded server

It is generally slower than a slice test because it loads a broader application context.

Comparison With Other Test Types

Test type Context loaded Typical use
@SpringBootTest Broad or full application context Integration and end-to-end testing
@WebMvcTest MVC-focused slice Controllers, request mapping and validation
@DataJpaTest Persistence-focused slice Repositories, queries and JPA mappings
@ContextConfiguration Explicit framework configuration Testing without Spring Boot auto-configuration

Slice tests provide fast, focused feedback, while @SpringBootTest is appropriate when the wiring between components is part of what must be tested.

Best Practices and Common Pitfalls

  • Keep full-context tests focused: Use slice tests for isolated component behavior and reserve @SpringBootTest for cross-component integration.

  • Manage test configuration: Use @ActiveProfiles("test"), @TestPropertySource, or @DynamicPropertySource rather than changing global configuration.

  • Avoid context pollution: Use @DirtiesContext only when a test genuinely requires a fresh context because it slows the test suite.

  • Use constructor injection: This makes dependencies explicit, supports immutable test fields and avoids field-injection problems.

  • Prefer current mocking APIs: In Spring Boot 3.5, @MockBean is deprecated; use Spring Framework 6.2’s @MockitoBean when a mocked bean is required.

  • Do not assume a real server runs: If the test makes network requests, explicitly select webEnvironment = RANDOM_PORT and inject the port with @LocalServerPort.

34

In which situations is Aspect-Oriented Programming appropriate within a Spring application?

Aspect-Oriented Programming (AOP) is appropriate in Spring when a cross-cutting behavior must be applied consistently across multiple parts of an application without mixing that behavior into the core business logic. Typical examples include logging, tracing, monitoring, auditing, and consistent error handling.

Read full answer

Applies toSpring Framework 6.2 and Spring Boot 3.5 on Java 17+

When to Use AOP

Use AOP when the same concern applies across several layers or operations:

  • Cross-cutting concerns: Record method-entry diagnostics, measure execution time, or report metrics consistently.

  • Auditing: Capture changes to entities, including the acting user, timestamp, and affected resource.

  • Operational tracing: Attach correlation IDs or instrumentation to application operations.

  • Policy enforcement: Apply a lightweight authorization or validation rule around selected methods.

  • Consistent exception translation: Convert infrastructure-specific exceptions into domain-level errors at a boundary.

  • Framework features: Spring already uses AOP internally for features such as @Transactional, @Cacheable, and @Async.

AOP is especially useful when the concern is technically separate from the primary responsibility of each method. For example, an order-processing method should implement order processing, while a separate aspect handles invocation metrics.

Spring AOP Model

Spring AOP is principally proxy-based. When a bean is advised, Spring creates a proxy that intercepts eligible method calls:

  1. The caller invokes the method through the proxy.

  2. The proxy executes matching advice.

  3. The actual bean method runs.

  4. The proxy processes the returned value or exception.

An aspect is normally defined with @Aspect, and advice is expressed using annotations such as @Before, @After, @AfterReturning, @AfterThrowing, or @Around. Pointcuts determine which methods are intercepted and can be based on annotations, packages, class names, or method signatures.

Spring AOP is not general bytecode weaving. It primarily applies to method calls made through a Spring-managed proxy.

When Not to Use AOP

Avoid AOP when the behavior is central business logic rather than a cross-cutting concern:

  • Put pricing rules, eligibility decisions, and workflow rules in explicit domain services.

  • Use Spring Security’s SecurityFilterChain and authorization mechanisms for HTTP security instead of implementing authentication in a general-purpose aspect.

  • Use Bean Validation for input constraints instead of scattering validation logic through advice.

  • Prefer ordinary inheritance, composition, decorators, or middleware when they are clearer for a single component.

Poorly scoped AOP can hide control flow, make execution order difficult to understand, and complicate debugging. An aspect that changes a method’s result or exception contract should be treated as a business-critical component.

Common Pitfalls

  • Self-invocation: If a method calls another advised method internally on this, the call may bypass the proxy. Move the call to another bean or use AspectJ weaving when interception of such calls is essential.

  • Proxy limitations: JDK dynamic proxies and Spring’s CGLIB-based class proxies cannot advise every possible method, including final methods in the class-proxy case.

  • Advice ordering: Multiple aspects can intercept the same method. Use @Order or Ordered deliberately and test the resulting order.

  • Exception handling: An @Around advice must propagate unexpected exceptions unless translating them intentionally.

  • Context propagation: Thread-local values may not behave as expected with asynchronous or reactive execution.

  • Overbroad pointcuts: Restrict advice to specific annotations, packages, or operations rather than intercepting most application methods.

Best Practices

  • Prefer Spring’s built-in @Transactional, @Cacheable, and @Async support over recreating their behavior.

  • Keep each aspect focused on one concern, such as metrics or auditing.

  • Use narrow, intention-revealing pointcuts, preferably annotation-based.

  • Avoid logging credentials, tokens, personal data, or sensitive request bodies.

  • Preserve the original method’s return value and exception semantics unless the aspect explicitly owns translation.

  • Use explicit dependency injection and constructor injection for aspect collaborators.

  • Treat AspectJ compile-time or load-time weaving as an advanced option for cases that require interception beyond Spring proxy boundaries.

35

What is Spring Integration, and in what scenarios is it most effectively used?

Spring Integration is a Spring-based framework for building message-driven integrations by connecting components through channels. It is especially useful when an application needs reliable, asynchronous, or event-driven communication with other systems.

Read full answer

Applies toSpring Integration 6.5, Spring Boot 3.5, Spring Framework 6.2, Java 17+

How It Works

Spring Integration models an integration flow as a collection of message endpoints connected by channels:

  • Messages: Data traveling through the flow, often represented by Message<?> objects.

  • Channels: Transport messages between components. Direct channels invoke the next component synchronously; executor channels can provide asynchronous processing.

  • Endpoints: Components that receive or send messages, such as adapters, routers, filters, transformers, and handlers.

  • Adapters: Connect Spring Integration to external systems or protocols, including HTTP, files, mail, messaging systems, and databases.

  • Message routers: Select the destination based on message content or headers.

  • Filters: Allow or reject messages according to business criteria.

  • Transformers: Convert a message into another representation.

The framework also provides a fluent Java DSL for defining these flows without relying heavily on XML.

Suitable Applications

Spring Integration is most appropriate when integration requirements go beyond making a single HTTP or database call:

  • Enterprise system integration: Connecting ERP, CRM, payment, fulfillment, and legacy systems.

  • Event-driven architectures: Processing domain events, notifications, audit records, and background work.

  • Protocol and format translation: Converting between CSV, XML, JSON, files, and message formats.

  • Asynchronous workflows: Decoupling producers from consumers and controlling concurrency with executor channels.

  • Resilient file processing: Reading, transforming, and writing files with retries and explicit error handling. Design consumers to be idempotent because ordinary file I/O does not automatically provide database-style transactional guarantees or atomic file moves.

  • Messaging-system integration: Bridging systems through supported messaging adapters.

  • Batch and data-processing pipelines: Combining it with Spring Batch for chunk-oriented processing and external integration.

For example, an order service could publish an OrderCreated event. Spring Integration could route that event to an inventory service, persist an audit record, send a customer notification, and move failures to a recovery channel.

Advantages and Limitations

Advantages Limitations
Reduces direct coupling between systems Adds framework components and configuration complexity
Supports asynchronous, event-driven designs Requires careful handling of retries, duplicate delivery, and ordering
Includes reusable adapters and routing patterns Is heavier than a direct RestClient call for simple integrations
Supports error handling and transactional workflows Incorrect channel or executor configuration can cause thread starvation or data loss

Spring Integration should not be the default choice for every integration. If one service simply needs to retrieve data from another service, RestClient in Spring Framework is usually simpler. Spring Integration becomes valuable when the workflow needs routing, transformation, isolation, retries, multiple destinations, or protocol adaptation.

Best Practices and Common Pitfalls

  • Keep channels and endpoints focused on a single business responsibility.

  • Use executor channels for genuinely independent background work; avoid using them for sequential steps that require immediate ordering.

  • Configure explicit retry, timeout, and dead-letter behavior rather than allowing failures to disappear.

  • Treat messages as potentially duplicated and design consumers to be idempotent.

  • Avoid placing large payloads directly in messages; pass identifiers or references when practical.

  • Prefer Spring Boot’s managed dependencies and official adapters so versions remain compatible.

  • Use Spring Integration with Spring Batch when a batch job requires external reads, writes, or transformations.

  • Do not confuse Spring Integration with Spring Messaging. Spring Messaging provides a lightweight messaging abstraction, while Spring Integration supplies a broader integration framework built on it.

36

How do endpoints function within Spring Integration, and which types are available?

An endpoint is a Spring Integration component that connects a message flow to another component or an external system. Endpoints can publish messages to channels, consume messages from channels, or exchange request-reply messages with external systems.

Read full answer

Applies toSpring Integration 6.5 with Spring Boot 3.5 and Spring Framework 6.2 on Java 17+

How Endpoints Work

Spring Integration flows commonly combine three roles:

  • Channel: Transports messages between components without containing the business processing logic.

  • Endpoint: Performs an integration or processing operation at a flow boundary.

  • Channel adapter: A specialized endpoint that translates between Spring Integration messages and an external file system, database, message broker, or HTTP service.

An endpoint may operate in one of two directions:

  • Producer-side: Polls or receives events from an external source, converts them into Message instances, and publishes them to a channel. An inbound channel adapter follows this pattern.

  • Consumer-side: Receives a message from a channel and processes it or sends it to an external target. Outbound adapters, service activators, filters, and routers follow this pattern.

A typical flow has an inbound adapter, one or more processing components, and an outbound adapter. Channels decouple these stages, allowing filtering, routing, monitoring, and error handling to be inserted between them.

Endpoint Families

Endpoint family Direction Behavior
Inbound channel adapter External system to channel Polls or subscribes to a source and publishes messages to a channel
Outbound channel adapter Channel to external system Consumes a message and writes or sends it to a target system
Inbound gateway External caller to channel Accepts an external request, forwards it into a flow, and can return the reply
Outbound gateway Channel to external system Invokes an external service and makes its response available to the flow
Message handler Channel to application logic Invokes a service or performs a handler operation
Message transformer Channel to transformed message Converts a payload or the complete Message into another representation
Message filter Channel to conditional route Passes or rejects a message according to a predicate
Message router Channel to selected channel(s) Chooses one or more destinations based on message content or other criteria
Message splitter One message to multiple messages Splits a message into a collection of messages for further processing
Message aggregator Multiple messages to grouped output Collects related messages and produces an aggregate or correlation result

These can be configured with the Java DSL or through mechanisms such as @ServiceActivator and @Router annotations. DSL operations such as transform(), filter(), split(), aggregate(), route(), and handle() make the flow’s relationships explicit.

Polling, Event-Driven, and Reactive Styles

  • Polling endpoints periodically inspect a source, such as a directory or database table. They are useful when the source cannot notify the application or when controlled polling is required.

  • Event-driven endpoints react when a source publishes an event or notification. They generally have lower latency but require a source that supports subscriptions, callbacks, or listener-style APIs.

  • Reactive endpoints can integrate with reactive messaging or HTTP systems using non-blocking processing. Blocking I/O should not be performed on an event-loop thread.

The same logical operation can therefore have polling, event-driven, or reactive implementations. Endpoint selection should follow the source’s capabilities and the application’s consistency and latency requirements.

Configuration and Operational Guidance

  • Keep channels explicit: Use separate channels when stages require independent routing, monitoring, filtering, or error handling.

  • Make consumers idempotent: Redelivery can occur after a failure, so an endpoint should avoid repeating an external side effect unintentionally.

  • Configure retries deliberately: Apply retry and recovery advice to transient failures, while routing persistent errors to an error channel or another recovery path.

  • Align transaction boundaries: A transaction boundary should reflect the actual resource operation; an HTTP call and a database update may require different consistency strategies.

  • Validate external input: Validate and, where appropriate, encode values before processing or forwarding them.

  • Avoid blocking reactive paths: Keep non-blocking execution semantics in reactive and asynchronous flows.

  • Distinguish integration endpoints from controllers: Spring Integration HTTP endpoints participate in message flows, whereas Spring MVC and WebFlux controllers primarily handle application-level HTTP requests.

37

How does Spring Batch divide a step into transactionally processed groups, and what happens when an item fails?

Chunk processing is a Spring Batch strategy that reads, processes, and writes a bounded group of items in a single transaction. The chunk size therefore determines both the amount of work held in memory and the transaction boundary used for commits and rollbacks.

Read full answer

Applies toSpring Batch 5.2 with Spring Boot 3.5, Spring Framework 6.2, Java 17+

How It Works

A chunk-oriented step contains three main components:

Component Responsibility
ItemReader Retrieves input items, usually up to the configured chunk size.
ItemProcessor Optionally validates or transforms each item.
ItemWriter Writes processed items to the destination.

Spring Batch repeatedly executes the following sequence:

  1. Starts a transaction.

  2. Reads up to the configured number of items.

  3. Processes each item through the optional ItemProcessor.

  4. Writes the resulting items through the ItemWriter.

  5. Commits the transaction.

  6. Begins the next chunk.

The processing of chunks continues until the reader reaches the end of its input. For example, a step with a chunk size of 100 can read 100 database records, validate and transform them, write them to another database, and commit all 100 records together.

Transaction and Failure Behavior

If an unhandled exception occurs while reading, processing, or writing a chunk, the transaction is normally rolled back and the step fails. For database output, this means the failed chunk’s writes are undone, while previously committed chunks remain committed.

The framework also supports controlled error handling:

  • Retry: A configured retry policy can attempt a failed processor or writer operation again.

  • Skip: A skip policy can exclude a problematic item and allow the step to continue. A skipped processor item is not passed to the writer.

  • Restart: After a failure, a restarted job normally resumes from the last committed chunk boundary. The failed chunk may be read again because its transaction was rolled back.

These behaviors are configured independently of the chunk size. A smaller chunk does not inherently provide retries or skips; explicit fault-tolerance policies are required.

Chunk Size Selection

The chunk size is a compromise between throughput and transaction overhead:

  • Larger chunks reduce transaction and job-repository overhead but hold more items and database locks during processing.

  • Smaller chunks limit failure scope and memory usage but increase the number of commits.

  • Chunk size is not an input-validation size: one invalid record can still cause the entire chunk to roll back unless it is skipped.

  • Writer performance matters: writing one database row per chunk is inefficient, so the writer should normally use batching capabilities such as JDBC batch operations.

For large database inputs, a paging or cursor-based reader is usually preferable to a reader that loads the entire source into memory.

Advantages and Limitations

Advantages:

  • Provides clear transaction boundaries without writing transaction-management code manually.

  • Limits memory consumption because only one chunk is processed at a time.

  • Enables efficient batch writes and periodic checkpoints.

  • Works well with fault tolerance and restartable job execution.

Limitations:

  • A failure can repeat work for the entire uncommitted chunk.

  • Large chunks can create long-running transactions and increase lock contention.

  • Rollback guarantees depend on the destination supporting transactional writes. A filesystem writer, for example, cannot automatically undo a file that was partially written unless the application implements that behavior.

  • Chunk processing alone does not create a distributed transaction across multiple remote systems.

When to Use It

Use chunk processing when a step processes a potentially large, finite collection of items and the destination can write those items transactionally. It is the usual choice for database-to-database batch jobs, record transformation pipelines, and bounded file imports.

A tasklet-oriented step is generally more suitable for a short operation that fits in one transaction, such as calling one stored procedure or performing a small amount of custom work. Chunk processing provides better restart behavior, memory control, and throughput for repeatable, item-based workloads.

38

How does Spring Cloud Data Flow coordinate and execute stream-processing applications?

Spring Cloud Data Flow coordinates the deployment and lifecycle of stream-processing applications but does not perform the stream processing itself. It launches Spring Boot applications, supplies their binding configuration, connects them through Spring Cloud Stream binders, and provides deployment, scaling, and task-management operations.

Read full answer

Applies toSpring Cloud Data Flow 2.12.x with Spring Boot 3.2.x, Spring Cloud Stream 4.1.x, Spring Framework 6.1.x, and Java 17+

How Coordination Works

A typical stream contains three kinds of applications:

  • Source: Produces events from an external system or messaging broker.

  • Processor: Transforms, filters, aggregates, or routes events.

  • Sink: Consumes results and writes them to a database, broker, file, or external service.

Spring Cloud Data Flow coordinates a stream through the following workflow:

  1. A stream definition arrives through the Spring Cloud Data Flow Shell, Dashboard, or REST API.

  2. The server validates the definition, resolves the referenced applications, and selects the platform-specific deployer.

  3. The deployer launches each source, processor, and sink application with the required deployment and binding properties.

  4. Data Flow supplies the destination and related binding configuration. Each application then uses its Spring Cloud Stream binder to create runtime producers and consumers.

  5. The server records deployment state and exposes lifecycle, scaling, and operational operations.

The applications form the data plane; Spring Cloud Data Flow acts primarily as the orchestration and control plane.

Main Components

Component Responsibility
Data Flow Server Stores stream and task definitions, deployment information, and execution metadata while exposing administrative APIs.
Stream Deployer Deploys and launches the applications belonging to a stream on the selected execution platform.
Task Launcher Starts task applications through the execution platform and enables their execution status to be tracked.
Binder Runs with deployed applications and maps their input and output channels to broker destinations such as topics, queues, or exchanges.
Application Registry Stores application metadata and version information. Deployable artifacts are obtained from the configured Maven or artifact repository or container registry, depending on the deployer.

Platform-specific implementations differ. The local deployer launches applications on the local machine, while the Kubernetes deployer creates Kubernetes resources and manages platform-specific deployment behavior.

Streams and Tasks

A stream is a continuously running data path, such as:

http-source > uppercase > log-sink

Data Flow deploys the applications separately, supplies their input and output configuration, and lets the Spring Cloud Stream binders connect them through the selected messaging system.

A task is normally a one-off, batch-style application execution rather than a continuous processing graph. It is submitted to a compatible Task Launcher, which starts it through the target platform; the platform reports status and the Data Flow server makes that information available through its APIs. Scheduling a task requires an external scheduler or platform-specific scheduling facility.

Spring Cloud Stream applications can use imperative listeners or the functional binding model, including reactive programming where supported by the binder.

Runtime Semantics

Delivery, ordering, retries, error handling, and state recovery depend on the binder, broker, and application configuration:

  • Delivery: An at-least-once configuration can produce duplicates, so processors should generally be idempotent.

  • Ordering: Kafka preserves order within a partition, but not across partitions.

  • Scaling: Additional application instances usually add consumers, increasing processing parallelism when the destination and consumer-group configuration permit it.

  • Backpressure: Behavior is governed by broker limits, binder settings, and consumer configuration.

  • State: Instances may maintain local state. Spring Cloud Data Flow does not provide a universal distributed checkpointing or state-management mechanism.

  • Recovery: Replay, redelivery, and dead-letter handling require support from the source, binder, broker, and application.

Best Practices and Common Pitfalls

  • Pin compatible versions: Use the Spring Boot, Spring Framework, Spring Cloud Stream, and Spring Cloud Data Flow versions supported by the selected release.

  • Distinguish control and data planes: Do not treat Data Flow as a computation engine; the deployed applications perform the processing.

  • Design for duplicate delivery: Use idempotent updates, deduplication keys, or appropriate transactional boundaries.

  • Configure errors explicitly: Set broker-specific retry, redelivery, and dead-letter policies rather than relying on implicit behavior.

  • Keep bindings observable: Monitor consumer lag, processing latency, error counts, application health, and broker capacity.

  • Avoid platform assumptions: Deployment commands, health checks, scaling, configuration injection, and secret management differ between local and Kubernetes environments.

39

What is Java Message Service (JMS), and how does Spring support messaging with JMS providers?

JMS (Java Message Service) is an API for exchanging messages asynchronously between producers and consumers through a messaging broker. Spring supports it through Spring JMS, providing message templates, listener containers, annotation-driven consumers, message conversion, exception translation, and transaction integration.

Read full answer

Applies toSpring Boot 3.5, Spring Framework 6.2 including Spring JMS, Java 17+, Jakarta Messaging API 3.1, Apache ActiveMQ Artemis 2.x

How JMS Works

A JMS application communicates with a broker rather than directly with other applications:

  • Producer: Creates and sends a message to a queue or topic.

  • Broker: Routes the message, stores it temporarily or persistently, and manages delivery.

  • Consumer: Receives and processes the message.

JMS defines standard message types such as TextMessage, ObjectMessage, BytesMessage, MapMessage, and StreamMessage. Applications can also use message properties, delivery modes, priorities, time-to-live values, and message selectors.

JMS provides asynchronous and loosely coupled communication, but delivery is not necessarily exactly once. With durable subscriptions and at-least-once delivery, consumers must be prepared to receive duplicates.

Queue and Topic Models

Model Broker behavior Typical use
Point-to-point queue Each message is delivered to one consumer; multiple consumers can share the workload Work distribution and background processing
Publish-subscribe topic Each active subscription receives its own copy of a published message Event notification and fan-out
Durable topic subscription The broker retains messages for an inactive durable subscriber Events that must survive temporary consumer outages

The same destination name represents different broker resources depending on whether it is defined as a queue or a topic.

Spring JMS Support

The spring-jms module integrates the JMS API with Spring’s dependency injection, configuration, conversion, transaction, and lifecycle mechanisms.

  • JmsTemplate: Provides high-level operations such as convertAndSend, receive, and receiveAndConvert. It translates checked JMS exceptions into Spring’s unchecked JmsException hierarchy.

  • @JmsListener: Invokes a listener method when a message arrives. A container factory determines the connection, concurrency, acknowledgment, and transaction behavior.

  • MessageListenerContainer: Manages broker connections, sessions, consumer threads, polling, startup, shutdown, and recovery.

  • Message converters: Convert between Java payloads and JMS message bodies. The same converter should normally be configured for producers and consumers.

  • Transaction integration: A transacted listener can roll back its JMS session when processing fails. Distributed database-and-message operations require JTA/XA rather than only a local JMS transaction.

Spring Boot adds auto-configuration for the broker ConnectionFactory, JmsTemplate, listener factory, and broker-specific properties. The spring-boot-starter-artemis starter configures an Artemis client and does not start an external broker. A typical setup supplies the broker URL and credentials through environment variables or a secrets manager rather than embedding them in source control.

Best Practices and Common Pitfalls

  • Provision the queue or topic on the broker before publishing.

  • Use queues for competing work and topics for event fan-out.

  • Make consumers idempotent because redelivery and duplicate processing are possible.

  • Configure bounded retries and a dead-letter strategy for repeatedly failing messages.

  • Do not assume a local JMS transaction provides atomicity across a database transaction and the broker; use JTA/XA for that requirement.

  • Keep message payloads small and use message properties for metadata that consumers need to filter or inspect.

  • Secure broker connections with credentials supplied through environment variables or a secrets manager, and use TLS when traffic crosses an untrusted network.

40

What function does the `@KafkaListener` annotation serve in a Spring application?

@KafkaListener is Spring Kafka’s declarative annotation for registering a method as a Kafka message-listener endpoint. Spring connects that endpoint to a KafkaMessageListenerContainer, which manages consumer polling, record deserialization, method invocation, offset handling, and listener error handling.

Read full answer

Applies toSpring Boot 3.5.x, Spring Framework 6.2.x, and Spring Kafka 3.3.x on Java 17+

How It Works

  • Spring discovers methods annotated with @KafkaListener during bean post-processing and registers each method as a listener endpoint.

  • The associated KafkaMessageListenerContainer creates and manages the Kafka consumers, subscribes them to the configured topics, polls records, and invokes the annotated method.

  • A listener method can receive the deserialized payload, a ConsumerRecord, a Spring Message, individual record headers through @Header, or an Acknowledgment when manual offset handling is enabled.

  • With Spring Boot’s Kafka auto-configuration and spring-boot-starter-kafka, the consumer factory, listener container factory, and annotation processing are configured automatically. In a plain Spring application, @EnableKafka enables listener annotation processing.

Important Options

Attribute Purpose
topics One or more topics consumed by the listener
topicPattern A regular expression used to select topics
groupId Consumer group used by the listener
id Identifier of the listener endpoint and its container
containerFactory Selects a specific KafkaMessageListenerContainerFactory bean
concurrency Number of consumer threads the endpoint may use
ackMode Controls offset-commit behavior for the endpoint
errorHandler Defines retry, recovery, and offset-handling behavior
autoStartup Controls whether the container starts automatically

A listener should normally specify an intentional groupId. If groupId is omitted, Spring Kafka can use the listener id as the consumer’s group.id, but relying on that implicitly can make group membership less obvious. Different consumer groups receive the same topic records independently, while members of one group divide the topic’s partitions among themselves.

Delivery and Failure Behavior

Offset management is separate from business-logic execution. Depending on the acknowledgement mode, offsets may be committed after one record or a batch is processed, periodically, after a configured number of records, or manually through an Acknowledgment.

If a listener throws an exception, the container delegates the failure to its configured error handler. An error handler may retry the record, recover it through another mechanism such as a dead-letter topic, or take another offset-handling action. Deserialization failures require particular care because repeatedly retrying an unreadable record can prevent the consumer from making progress.

Kafka’s delivery model permits duplicate processing. For example, a consumer may process a record and fail before its offset is committed, after which another consumer can receive that record. Business operations should therefore be idempotent, using a unique event identifier, database uniqueness constraint, or idempotency table.

Best Practices and Common Pitfalls

  • Use constructor injection for listener dependencies so they can be validated and tested normally.

  • Make handlers idempotent because business processing and offset commits are not one atomic operation.

  • Keep processing within Kafka’s polling limits. Long-running work can exceed max.poll.interval.ms and cause a consumer-group rebalance.

  • Configure failure recovery explicitly. Retries should be bounded, with an appropriate recovery destination for persistent failures.

  • Treat concurrency as an upper bound: three listener consumers cannot process three partitions simultaneously if the topic has fewer partitions or those partitions are assigned elsewhere.

  • Avoid manual polling in the listener method; @KafkaListener already delegates polling and consumer-thread management to Spring’s container.

41

How can you use Spring AMQP to publish messages to a broker and consume them with a RabbitMQ listener?

Use Spring AMQP with a RabbitMQ broker. Producers send messages through RabbitTemplate, while @RabbitListener runs a consumer method when messages arrive on a configured queue. Spring Boot can automatically configure the broker connection, templates, listeners, and queue declarations.

Read full answer

Applies toSpring Boot 4.0, Spring Framework 7.0, Spring AMQP 4.0, RabbitMQ 4.x, and Java 17+

How It Works

Spring AMQP provides an AMQP client abstraction; RabbitMQ is the broker implementation. A typical message flow is:

  1. The producer sends a message to an exchange using RabbitTemplate.

  2. The exchange routes the message to a queue according to its binding and routing key.

  3. A listener container connects to RabbitMQ and invokes a method annotated with @RabbitListener.

  4. In the default AUTO acknowledgment mode, the container acknowledges the message after the listener returns successfully.

The exchange, queue, and binding should normally be declared as application beans so the topology is created consistently across environments.

Key Components

  • RabbitTemplate: Sends messages and receives replies when configured with receive or receiveAndReply.

  • RabbitListener: Creates a listener container and delegates incoming messages to a Java method.

  • Exchange: Receives published messages and routes them. The example uses a DirectExchange.

  • Queue: Stores messages until a consumer processes them.

  • Binding: Connects an exchange to a queue with a routing key.

  • SimpleMessageConverter: The default converter; it supports simple values such as String and byte arrays. JSON object conversion requires an appropriate message converter.

Code Example

Add spring-boot-starter-amqp, which is managed by the Spring Boot dependency versions.

Configure connection details through environment variables rather than storing credentials in source control:

spring:
  rabbitmq:
    host: ${RABBITMQ_HOST:localhost}
    port: ${RABBITMQ_PORT:5672}
    username: ${RABBITMQ_USERNAME}
    password: ${RABBITMQ_PASSWORD}
    listener:
      simple:
        default-requeue-rejected: false

The following classes can be placed in the same package, with each class in its own source file:

package com.example.orders;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class AmqpTopology {

    @Bean
    DirectExchange orderExchange() {
        return new DirectExchange("orders.exchange", true, false);
    }

    @Bean
    Queue orderQueue() {
        return new Queue("orders.queue", true);
    }

    @Bean
    Binding orderBinding(Queue orderQueue, DirectExchange orderExchange) {
        return BindingBuilder.bind(orderQueue)
                .to(orderExchange)
                .with("order.created");
    }
}

Publish a message with constructor injection:

package com.example.orders;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
class OrderPublisher {

    private final RabbitTemplate rabbitTemplate;

    OrderPublisher(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void publishOrderCreated(String orderId) {
        rabbitTemplate.convertAndSend(
                "orders.exchange",
                "order.created",
                orderId
        );
    }
}

Consume the message on a RabbitMQ queue:

package com.example.orders;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
class OrderMessageListener {

    @RabbitListener(queues = "orders.queue")
    public void handleOrderCreated(String message) {
        // Persist or otherwise process the order.
        System.out.println("Received order: " + message);
    }
}

Delivery Semantics and Reliability

  • RabbitTemplate.convertAndSend() hands the message to the AMQP client for transmission; it does not wait for a RabbitMQ publisher confirm or guarantee durable persistence.

  • To observe broker confirmations, set spring.rabbitmq.publisher-confirm-type=correlated and use CorrelationData or a confirm callback. A confirmation indicates that the broker accepted responsibility for the message, but its durability still depends on the queue and message configuration.

  • To detect unroutable messages, enable spring.rabbitmq.publisher-returns=true, ensure mandatory publishing, and register a returns callback. Boot configures its RabbitTemplate as mandatory when publisher returns are enabled.

  • With the default AUTO acknowledgment mode, acknowledgment occurs after the listener completes successfully. If the listener throws an exception, the container applies its requeue and rejection policy.

  • default-requeue-rejected: false prevents an endlessly failing message from being returned to the queue immediately and rejected. For transient failures, configure bounded retries and send exhausted messages to a dead-letter queue.

  • Duplicate delivery is still possible. Consumers should be idempotent, especially when combined with publisher confirms or redelivery.

Best Practices and Common Pitfalls

  • Use constructor injection, rather than field injection with @Autowired.

  • Define exchanges, queues, and bindings as durable application beans when they are application-owned.

  • Do not rely on @RabbitListener for JSON objects without configuring a JSON message converter.

  • Keep listener methods focused and delegate business processing to a separate service.

  • Avoid naming the exchange, queue, or routing key inconsistently; constants can prevent publishing and binding mismatches.

  • Do not acknowledge a message manually before processing finishes. Use manual acknowledgment only when the processing and acknowledgment sequence can be controlled correctly.

42

How can Spring applications manage Kafka transactions for producers and consumer-based workflows?

Spring manages Kafka transactions with a KafkaTransactionManager, allowing Kafka records—and, in consumer workflows, consumed offsets—to be committed atomically. Depending on the scenario, transactions can coordinate multiple sends, consume-transform-produce processing, or provide read-committed delivery to downstream consumers.

Read full answer

Applies toSpring Boot 4.0, Spring Framework 7.0, and Spring Kafka 4.0 on Java 17+

How Kafka Transactions Work

Kafka transactions provide atomicity for records written to one or more Kafka topics:

  • Producer transaction: Multiple send() operations either all become visible or none do.

  • Consumer transaction: A listener can produce an output record and commit its consumed offset in the same Kafka transaction.

  • Isolation level: Consumers configured with read_committed only receive committed transactional records.

  • Scope: The guarantee applies to Kafka operations. It does not automatically include databases, HTTP calls, files, or other external systems.

Spring provides this behavior through KafkaTransactionManager, a PlatformTransactionManager implementation that works with Spring’s @Transactional support.

Producer Transactions

Define a KafkaTransactionManager using the application’s ProducerFactory:

@Bean
KafkaTransactionManager<String, String> kafkaTransactionManager(
        ProducerFactory<String, String> producerFactory) {
    return new KafkaTransactionManager<>(producerFactory);
}

The producer factory must be transaction-capable. In Boot configuration, this is enabled with spring.kafka.producer.transaction-id-prefix. Spring Kafka then generates a unique transactional.id for the producer instance.

Inside a @Transactional method, all Kafka sends performed through KafkaTemplate participate in one transaction. The transaction commits when the method exits normally and rolls back if an unchecked exception escapes it.

Consumer-Process-Producer Transactions

A listener container can own the Kafka transaction. When the container is configured with a KafkaAwareTransactionManager, Spring Kafka:

  1. Starts a Kafka transaction before invoking the listener.

  2. Publishes any records sent by the listener through KafkaTemplate.

  3. Writes the consumed offset into the same transaction.

  4. Commits both the output record and offset after successful processing.

  5. Aborts the transaction if listener processing fails.

This pattern is commonly called read-process-write and supports effectively-once processing within Kafka.

Code Example

The following configuration uses separate transaction managers for ordinary producer calls and listener-container transactions. This keeps their transaction ID prefixes distinct.

spring:
  kafka:
    bootstrap-servers: localhost:9092
    producer:
      transaction-id-prefix: orders-producer-
      acks: all
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      properties:
        enable.idempotence: true
    consumer:
      group-id: order-processors
      isolation-level: read_committed
      auto-offset-reset: earliest
package com.example.orders;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.listener.ContainerProperties;
import org.springframework.kafka.transaction.KafkaTransactionManager;

@Configuration
@EnableKafka
public class KafkaTransactionConfiguration {

    @Bean
    KafkaTransactionManager<String, String> kafkaTransactionManager(
            ProducerFactory<String, String> producerFactory) {
        return new KafkaTransactionManager<>(producerFactory);
    }

    @Bean
    KafkaTransactionManager<String, String> kafkaListenerTransactionManager(
            ProducerFactory<String, String> producerFactory) {
        return new KafkaTransactionManager<>(producerFactory);
    }

    @Bean
    ConcurrentKafkaListenerContainerFactory<String, String>
            kafkaListenerContainerFactory(
                    ConsumerFactory<String, String> consumerFactory,
                    KafkaTransactionManager<String, String>
                            kafkaListenerTransactionManager) {
        var factory =
                new ConcurrentKafkaListenerContainerFactory<String, String>();
        factory.setConsumerFactory(consumerFactory);
        factory.getContainerProperties()
                .setKafkaAwareTransactionManager(kafkaListenerTransactionManager);
        factory.getContainerProperties()
                .setTransactionIdPrefix("orders-listener-");
        factory.getContainerProperties()
                .setAckMode(ContainerProperties.AckMode.RECORD);
        return factory;
    }
}

A transactional producer can publish related records atomically:

package com.example.orders;

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderEventPublisher {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public OrderEventPublisher(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    @Transactional(transactionManager = "kafkaTransactionManager")
    public void publishOrder(String orderId, String payload) {
        kafkaTemplate.send("orders", orderId, payload).join();
        kafkaTemplate.send("order-audit", orderId, payload).join();
    }
}

A listener can atomically publish its result and commit the consumed offset:

package com.example.orders;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class OrderEventListener {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public OrderEventListener(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    @KafkaListener(
            id = "order-processor",
            topics = "orders",
            groupId = "order-processors",
            containerFactory = "kafkaListenerContainerFactory")
    @Transactional(transactionManager = "kafkaListenerTransactionManager")
    public void process(ConsumerRecord<String, String> record) {
        kafkaTemplate.send(
                "processed-orders", record.key(), record.value()).join();
    }
}

Best Practices and Common Pitfalls

  • Wait for send results: Using join() surfaces asynchronous send failures before the transaction exits. Otherwise, a failure may occur after the method appears to have succeeded.

  • Do not swallow exceptions: Rethrow failures so the listener transaction can abort and the offset can be retried.

  • Use read_committed: Consumers that must not observe aborted transactions should set isolation.level=read_committed.

  • Keep transaction IDs unique: Two active producers using the same transactional.id fence one another. Use stable, distinct prefixes for each logical producer.

  • Configure the broker correctly: A single-node development cluster normally needs transaction.state.log.replication.factor=1 and transaction.state.log.min.isr=1; retain replicated defaults in production.

  • Do not expect atomic database writes: A Kafka transaction cannot atomically update Kafka and a relational database. Use a transactional outbox or inbox pattern for reliable cross-system coordination.

  • Expect retries and duplicates outside Kafka: External side effects should generally be idempotent because the overall application workflow may still be retried.

44

What facilities does Spring provide for implementing GraphQL APIs, and how are schemas, resolvers, HTTP transport, and testing handled?

Spring for GraphQL integrates the GraphQL Java execution engine with Spring Boot’s configuration, dependency injection, validation, HTTP infrastructure, security, and testing support. Applications define a GraphQL schema and implement its fields with annotated Spring controllers or other Spring-managed components.

Read full answer

Applies toSpring Boot 3.5.x with Spring Framework 6.2.x, Spring GraphQL 1.4.x, and GraphQL Java 24.x on Java 17+

Schemas and Resolvers

  • Schema-first development: SDL files can be placed under the classpath graphql/ directory, commonly as src/main/resources/graphql/schema.graphqls. Spring GraphQL loads these files and creates the executable GraphQLSchema.

  • Code-first development: An application can instead provide a GraphQLSchema bean and configure the mapping between schema fields and data-fetching code using GraphQL Java runtime wiring.

  • @QueryMapping and @MutationMapping: Map methods to fields of the schema’s Query and Mutation types.

  • @SchemaMapping: Maps a method to a field on any schema type when more specific query or mutation annotations are unnecessary.

  • @SubscriptionMapping: Handles subscription fields and supports streaming responses.

  • @Argument: Binds a GraphQL field argument to a method parameter. Jakarta Bean Validation constraints can validate input values.

  • @BatchMapping: Resolves a field for a collection of parent objects in one invocation, helping prevent N+1 database queries.

A schema is the public contract. Resolvers should normally delegate to application services rather than containing business rules or transaction logic.

Execution and Data Fetching

The typical execution flow is:

  1. The client submits a GraphQL operation over HTTP.

  2. GraphQL Java parses the document and validates it against the configured schema.

  3. Spring GraphQL identifies the resolver for each selected field.

  4. The resolver or a batched data loader retrieves the required data.

  5. Spring GraphQL serializes the result into a JSON response containing data, errors, or both.

Spring Data repositories, JDBC, JPA, reactive clients, and ordinary service beans can all sit behind resolvers. For repeated lookups, @BatchMapping or GraphQL Java’s DataLoader mechanism can batch and cache data within the execution of one operation.

HTTP, WebSocket, and Security

  • Servlet transport: With spring-boot-starter-web, GraphQL requests are handled through Spring MVC.

  • Reactive transport: With spring-boot-starter-webflux, requests are handled through WebFlux.

  • Default endpoint: Boot exposes GraphQL at /graphql by default.

  • Subscriptions: Subscription resolvers can be exposed over WebSocket when the application includes the appropriate WebSocket starter and transport configuration.

  • HTTP security: Spring Security protects GraphQL through the normal SecurityFilterChain mechanism.

  • Field authorization: Securing the endpoint does not automatically authorize individual fields or mutations. Permission checks must also be implemented in resolvers or their service layer.

  • Error handling: GraphQL execution errors are normally represented in the response’s errors array. HTTP status codes still communicate transport-level problems such as authentication failures or malformed requests.

Testing

Spring GraphQL provides several testing levels:

  • GraphQlTester: Executes GraphQL documents and asserts the returned data and errors without requiring a full HTTP server.

  • MockGraphQlService: Supports isolated tests by mocking GraphQL execution and the data supplied to resolvers.

  • HttpGraphQlTester: Sends real HTTP requests to a running GraphQL endpoint for end-to-end testing.

  • WebGraphQlTester: Tests a reactive GraphQL endpoint through WebTestClient.

  • MockMvc and WebTestClient: Can directly test the GraphQL HTTP endpoint in MVC and WebFlux applications respectively.

  • @GraphQlTest: Spring Boot’s focused test slice for testing GraphQL components with only the relevant infrastructure auto-configured.

A useful testing strategy combines resolver-level tests for business behavior, GraphQlTester tests for schema and field execution, and HTTP-level tests for transport, security, and content negotiation.

Best Practices and Common Pitfalls

  • Keep resolvers thin: Put business rules, transactions, and authorization decisions in dedicated services.

  • Use constructor injection: It makes dependencies explicit and supports immutable, testable components.

  • Avoid N+1 queries: Use @BatchMapping or DataLoader for fields fetched repeatedly.

  • Handle exceptions deliberately: Convert expected failures into safe GraphQL errors without exposing stack traces or sensitive information.

  • Apply field-level authorization: Protecting /graphql alone does not protect every operation.

  • Control query complexity: Apply limits for query depth, complexity, request size, and batching when untrusted clients are allowed.

  • Treat schema changes as API changes: Evolving a schema can affect clients even when the underlying REST resources remain unchanged.

45

What benefits can GraphQL provide over REST when developing APIs with Spring?

GraphQL lets clients request exactly the fields they need from a strongly typed API schema, whereas REST exposes fixed endpoints that return predefined representations. In Spring, Spring for GraphQL provides first-class support for defining, executing, securing, and testing GraphQL operations.

Read full answer

Applies toSpring Boot 3.5.x, Spring Framework 6.2.x, and Spring for GraphQL 1.4.x on Java 17+

How GraphQL Differs from REST

Aspect GraphQL REST
Endpoint design Usually one endpoint such as /graphql for queries and mutations Multiple resource-oriented URLs such as /orders/{id}
Response shape Determined by the client’s selection set Determined by the endpoint’s representation
Strong typing Schema defines queries, mutations, fields, arguments, and types Typically relies on conventions and external specifications such as OpenAPI
Over-fetching Clients request only required fields Responses may include fields the client does not need
Multiple resources A query can traverse related resources in one operation Usually requires several HTTP requests
HTTP caching More difficult because requests commonly use POST Naturally supported through HTTP caching headers and conditional requests
Observability Requires GraphQL-aware metrics and tracing Standard HTTP logs, metrics, and traces apply directly

Main Advantages

  • Precise data retrieval: Clients select individual fields, reducing unnecessary payload and avoiding many common over-fetching problems.

  • Efficient resource composition: One GraphQL operation can retrieve a user and related orders, posts, or other domain objects without manually chaining several REST calls.

  • Strong schema contract: The schema provides a centralized, strongly typed contract that supports code generation, validation, and IDE tooling.

  • Schema evolution flexibility: Additive fields are generally easier to introduce because existing clients do not automatically receive them.

  • Consistent query model: Query and mutation operations provide a uniform interface across different resources.

  • Server-controlled execution: Resolvers enforce authorization and access rules while returning only the data permitted for the current request.

  • Built-in ecosystem support: Spring for GraphQL integrates with Spring MVC or WebFlux, Spring Security, WebSocket subscriptions, testing, and observability.

Important Limitations

  • GraphQL does not automatically eliminate network round trips or server-side N+1 queries. Clients may still need several requests, and resolvers can perform inefficient database queries unless batching and caching are implemented correctly.

  • HTTP caching is less straightforward because most GraphQL queries use POST. Persisted queries, GET requests where appropriate, and cache-control design can help, but REST remains simpler for public, cache-heavy resources.

  • GraphQL moves complexity into schema design, resolver authorization, query-cost controls, error handling, and monitoring. A poorly designed GraphQL API can be harder to operate than a set of REST endpoints.

  • Large schemas and unrestricted queries can consume excessive server resources. Production systems commonly require depth limits, complexity limits, timeouts, and execution safeguards.

  • GraphQL is not automatically more efficient than REST. The eventual number of SQL queries, serialization work, and resolver calls still determines much of the performance.

Spring Support

Spring for GraphQL separates the API contract from the application implementation:

  • The schema defines operations, object types, inputs, arguments, and custom scalars.

  • @QueryMapping and @MutationMapping expose GraphQL operations.

  • @SchemaMapping supplies fields on schema types.

  • DataFetcher provides an alternative when field-level annotations are not suitable.

  • Spring Security can protect GraphQL requests and apply authorization rules to individual operations and fields.

  • Batch loading can be implemented with Spring for GraphQL’s batch APIs or a batching library to avoid repeated lookups.

  • WebSocket-based subscriptions are available for real-time events, although their deployment and scaling requirements differ from ordinary request-response APIs.

When to Prefer Each Approach

Choose GraphQL when several clients need flexible combinations of related data, response over-fetching is significant, or a strongly typed schema is important to the product.

Choose REST when resources are simple and stable, caching at the HTTP layer is critical, the API must expose conventional resource URLs, or integration with existing HTTP tooling and infrastructure is the priority.

A pragmatic Spring architecture can use both: REST for public or cache-oriented endpoints and GraphQL for application-specific, highly interconnected data access.

Sign in to keep reading

You've opened 2 full answers. Sign in to read all 46 Spring answers, mark what you know and pick up where you left off.

All Web and Mobile Development subjects