Interview questions

Spring Framework 3 Interview Questions: 3 Answers You Need for Legacy Spring Apps

August 28, 2025Updated May 9, 202616 min read
How Does Mastering Spring Framework 3 Unlock Your Full Potential In Tech Interviews?

Spring Framework 3 interview questions are usually about legacy XML-to-annotation migration, Spring MVC request flow, and the tradeoffs older apps still expose.

Legacy codebases don't disappear just because better frameworks exist. Spring Framework 3 interview questions keep surfacing in hiring screens not because companies are nostalgic, but because a lot of production systems still run on Spring 3-era code — and the teams maintaining them need engineers who won't break things while trying to modernize them.

The difference between a candidate who answers these questions well and one who stumbles isn't usually depth of Spring knowledge. It's whether they've thought about why older apps were configured the way they were. Someone who only knows Spring Boot can name the annotations. They fall apart when asked why a legacy app still uses XML-based bean wiring, or what breaks when you casually swap setter injection for constructor injection in a class that has a circular dependency.

This guide covers the six topic areas that come up most in legacy Spring interviews: configuration migration, IoC and dependency injection, bean scopes and lifecycle, Spring MVC request flow, AOP, and data access. For each one, there's a clear answer and the follow-up framing that separates people who've worked in these codebases from people who've only read about them.

Why Spring Framework 3 still shows up in interviews

Why do companies still ask about Spring 3 instead of just moving on?

The short answer: because they haven't moved on. A significant portion of enterprise Java applications — in banking, insurance, healthcare, and government — were built between 2009 and 2014, which is exactly when Spring 3.x was the production-grade choice. These apps aren't being rewritten. They're being maintained, extended, and occasionally migrated in pieces. When a team posts a job that mentions "Spring MVC" or "XML-based configuration," they're signaling that the codebase is older, and they need someone who won't be surprised by it.

Job postings in 2023 and 2024 still regularly list Spring MVC, `applicationContext.xml`, and "legacy Java EE integration" as explicit requirements. That's not a mistake — it's a description of the actual system. When a recruiter screens for Spring 3 interview questions, they're trying to identify candidates who can read a `beans.xml` file, understand what's wired where, and explain the configuration choices without having to Google every annotation.

The real risk these teams are managing is migration risk. A partial migration — where some beans are defined in XML and others are annotation-driven — is genuinely tricky to reason about. If you don't understand how component scanning interacts with explicitly-declared XML beans, you can introduce subtle bugs: duplicate beans, incorrect scope resolution, initialization ordering problems. Interviewers who ask about Spring 3 are often probing for exactly this: can you hold both configuration styles in your head at once?

Firsthand, the apps that are hardest to work in aren't the ones that are fully XML or fully annotation-driven. They're the ones halfway through a migration that stalled. A `DispatcherServlet` configured in `web.xml`, a mix of `@Component`-scanned services and explicitly declared `<bean>` entries, and a handful of `@Configuration` classes that were added later and don't quite align with the original XML structure. That's the real environment these spring framework 3 interview questions are probing for.

Spring 3 configuration: the XML-to-annotation shift interviewers care about

What changed in Spring 3, and why did profiles and SpEL matter?

Spring 3 configuration represents a genuine inflection point in how Spring apps were structured. Before Spring 3, the dominant pattern was pure XML: every bean declared explicitly in `applicationContext.xml`, every dependency wired by property or constructor-arg elements. Spring 3 didn't eliminate XML, but it made annotation-driven configuration a first-class option — and it introduced two features that changed how teams organized environment-specific behavior: bean definition profiles and the Spring Expression Language (SpEL).

Here's what the shift looked like in practice. A legacy XML-only bean definition looked like this:

The Spring 3 annotation-equivalent in the same app, after a partial migration, looked like this:

Both work. Both can coexist in the same application context. The problem is that coexistence requires explicit component scanning configuration — if the XML context doesn't include `<context:component-scan base-package="com.example"/>`, the annotated class won't be picked up, and you'll get a `NoSuchBeanDefinitionException` that looks mysterious if you don't know where to look.

Profiles (`@Profile` and the `<beans profile="">` XML attribute) mattered because they gave teams a way to swap entire bean configurations between environments without maintaining separate XML files. Before profiles, the common pattern was property placeholder substitution — workable, but fragile when the differences between environments went beyond connection strings. Profiles let you declare a `dataSource` bean for `dev` and a different one for `prod`, activated by a JVM argument or environment variable.

SpEL extended this further by letting bean property values reference other beans, call methods, or evaluate conditions at runtime. The Spring Framework documentation covers the full SpEL syntax, but in interview terms, the key point is that SpEL replaced a lot of brittle string-concatenation logic that older apps used to handle environment-specific configuration. A strong answer on Spring 3 configuration acknowledges both the capability and the maintenance cost: SpEL expressions in XML are powerful and nearly impossible to refactor safely without running the app.

How IoC and dependency injection actually work in Spring 3

How do IoC and dependency injection work in Spring 3, and which style should you prefer?

Spring 3 dependency injection supports three styles: constructor injection, setter injection, and field injection. Knowing all three isn't the interesting part of the interview answer. The interesting part is being able to explain which one legacy code typically used, why, and what the tradeoffs are when you try to change it.

Constructor injection declares dependencies as constructor parameters. The container calls the constructor with the resolved dependencies, and the resulting object is fully initialized. This is the preferred style for modern Spring development because it makes dependencies explicit, enables immutability (`final` fields), and makes the class trivially testable without a Spring context — you just call the constructor with mocks.

Setter injection was the dominant pattern in Spring 2.x and early Spring 3 apps. It works by declaring a no-arg constructor and then calling setter methods for each dependency. The XML wiring used `<property name="..." ref="..."/>` elements. The practical problem with setter injection is that it allows partially-initialized objects: a bean can be created but not fully wired, and if you call a method before all setters have been invoked, you get a `NullPointerException` that's hard to trace. Legacy apps that used setter injection heavily often have initialization order bugs that only surface under specific startup conditions.

Field injection — `@Autowired` directly on a private field — became common in Spring 3 annotation-driven code because it's the least verbose option. It's also the hardest to test. You can't inject a mock without either using a Spring test context or reaching into the field via reflection. The Spring team's own guidance has consistently discouraged field injection for this reason, though you'll still find it throughout Spring 3-era codebases.

A concrete migration example: a legacy `OrderService` with three setter-injected dependencies wired in XML. When migrating to annotations, the instinct is to add `@Autowired` to each field. The better move is to convert to constructor injection at the same time — but that only works if there are no circular dependencies. In a large legacy app, circular dependencies are common, and they're often the reason setter injection was used in the first place. A strong interview answer acknowledges this: "I'd prefer constructor injection, but in a legacy app I'd check for circular dependencies before making that change, because the setter injection may have been a deliberate workaround."

Bean scopes and bean lifecycle in older Spring apps

What bean scopes matter most, and when would singleton, prototype, request, or session be used?

Spring 3 bean lifecycle management starts with scope. The default scope is singleton: one instance per application context, shared across all injection points. This is correct for stateless services — a `UserService`, a `PaymentProcessor`, a `ReportGenerator`. Singleton beans are created once, wired once, and reused. The risk is state: if a singleton bean holds mutable state, every thread that uses it shares that state, which is a concurrency bug waiting to happen.

Prototype scope creates a new instance every time the bean is requested from the context. This matters when the bean holds per-request or per-operation state that shouldn't be shared. The catch: Spring doesn't manage the lifecycle of prototype beans after creation. It creates them and hands them off. `destroy-method` callbacks are never called on prototype beans, which surprises developers who assume the container handles cleanup.

Request and session scopes are specific to Spring MVC web applications. A request-scoped bean lives for the duration of a single HTTP request; a session-scoped bean lives for the duration of a user's HTTP session. These scopes were used in Spring 3 MVC apps for things like shopping cart state (session) or form-binding objects (request). The important interview point: injecting a request-scoped bean into a singleton requires a scoped proxy — otherwise the singleton gets a reference to the first request's bean and holds it forever. The XML configuration for this was `<aop:scoped-proxy/>` inside the bean definition.

How does the Spring 3 bean lifecycle work, and what happens during initialization and destruction?

The Spring 3 bean lifecycle follows a well-defined sequence. After the container instantiates the bean and injects its dependencies, it checks for lifecycle callbacks in this order: `@PostConstruct` methods, then `InitializingBean.afterPropertiesSet()`, then the `init-method` specified in XML. The same layered pattern applies to destruction: `@PreDestroy`, then `DisposableBean.destroy()`, then `destroy-method`.

In legacy XML-era apps, `init-method` and `destroy-method` were the standard approach because `@PostConstruct` wasn't always available or wasn't in the project's coding standards. A real bean definition from that era looked like this:

The `initialize()` method opened connections; `shutdown()` closed them. This is where legacy apps hide resource setup and cleanup — not in constructors, not in annotated methods, but in XML-declared lifecycle callbacks that only run if the application context is closed properly. If a legacy app has resource leaks, checking `destroy-method` implementations (or their absence) is one of the first places to look.

The Spring Framework reference documentation covers the full lifecycle in detail. For interview purposes, the key is being able to trace the sequence and explain where each type of initialization logic belongs — and why mixing `@PostConstruct` with XML `init-method` in the same bean is technically valid but a maintenance problem.

How DispatcherServlet and @RequestMapping fit together in Spring MVC

How does DispatcherServlet, handler mappings, and @RequestMapping fit together in Spring MVC?

Legacy Spring MVC interview questions almost always include some version of "walk me through what happens when a request comes in." The weak answer names the pieces. The strong answer traces the actual path.

The entry point for every request in a Spring MVC app is `DispatcherServlet`, declared in `web.xml`. It's a front controller: all requests matching its URL pattern flow through it. The `DispatcherServlet` loads its own application context (the "servlet context"), which typically includes the MVC configuration — handler mappings, view resolvers, and the controller beans. This is separate from the root application context, which holds services and repositories. Getting this hierarchy wrong is a common source of "bean not found" errors in Spring 3 apps.

Once a request arrives, `DispatcherServlet` consults its `HandlerMapping` implementations to find the right controller method. In Spring 3, the dominant mapping strategy was `DefaultAnnotationHandlerMapping`, which reads `@RequestMapping` annotations on controller classes and methods. The mapping resolves to a `HandlerExecutionChain` — the controller method plus any interceptors that apply to that path.

The `@RequestMapping` annotation on a method declares which HTTP method and path it handles. In Spring 3, this was the full configuration: path, method, params, headers, and content type could all be specified. The annotation-driven approach replaced the older `SimpleUrlHandlerMapping` and explicit controller registrations that Spring 2.x required.

After the handler executes, it returns a `ModelAndView`. The `DispatcherServlet` passes this to a `ViewResolver` — typically `InternalResourceViewResolver` in legacy apps — which maps the logical view name to a JSP. The Spring MVC documentation covers the full dispatch cycle. For interviews, the credibility marker is knowing that the request hits `DispatcherServlet` first, not the controller — and being able to explain what happens if no handler mapping matches (a 404, not an exception, unless configured otherwise).

AOP and JdbcTemplate are the two older Spring topics they still probe

What AOP concepts should a candidate be able to explain clearly in a Spring 3 interview?

Spring 3 interview questions on AOP tend to separate candidates quickly, because the terminology is easy to memorize and hard to explain without a concrete example. The four terms that matter: join point, pointcut, advice, and aspect.

A join point is a specific point in program execution — in Spring AOP, always a method execution. A pointcut is an expression that matches a set of join points: "all methods in the `com.example.service` package," for instance. Advice is the code that runs at a matched join point — before, after, or around the method. An aspect is the combination of a pointcut and one or more pieces of advice, packaged as a class.

The concrete example that makes this stop sounding academic: transaction logging in a legacy service layer. Instead of adding logging calls to every service method, a single aspect with a pointcut matching all `@Service` methods can log entry, exit, and execution time without touching the service classes. The same pattern applies to security checks, performance monitoring, and — most commonly in Spring 3 apps — transaction management. Spring's `@Transactional` annotation is itself an AOP-backed feature: a proxy wraps the annotated method and manages the transaction boundary around it.

The follow-up that catches people: "What's the difference between `@Before` and `@Around` advice?" Before advice runs before the method and can't prevent execution or modify the return value. Around advice wraps the method entirely — it can modify arguments, prevent the call, catch exceptions, and modify the return value. Transaction management uses around advice because it needs to commit or roll back based on what happens inside the method.

What should you know about JdbcTemplate, transactions, and basic data-access patterns in older Spring apps?

`JdbcTemplate` was the standard data-access tool in Spring 3-era apps that weren't using a full ORM. The motivation was straightforward: raw JDBC required explicit connection management, statement preparation, result set iteration, and exception handling in every DAO method. A single query could be 20 lines of boilerplate. `JdbcTemplate` collapsed that to 3–5 lines by handling connection acquisition, exception translation, and resource cleanup internally.

A typical legacy migration looked like this: a DAO that opened a `Connection`, created a `PreparedStatement`, iterated a `ResultSet`, and closed everything in a `finally` block became a single `jdbcTemplate.query()` call with a `RowMapper`. The `JdbcTemplate` instance was typically declared as a bean in XML and injected into each DAO — a pattern that's easy to recognize in legacy codebases.

Transaction management in Spring 3 apps used one of two approaches: XML-based `<tx:advice>` with AOP pointcuts, or `@Transactional` annotations on service methods. The XML approach was more common in older apps because it kept transaction configuration out of the business logic entirely. The annotation approach was introduced in Spring 2.0 and became dominant in Spring 3 apps built after 2009.

The weak answer on transactions describes `@Transactional` in isolation. The strong answer explains the propagation model: what happens when a transactional method calls another transactional method. The default propagation is `REQUIRED` — join the existing transaction if one exists, create a new one if not. `REQUIRES_NEW` suspends the outer transaction and starts a fresh one. Getting this wrong in a legacy app causes subtle bugs where a rollback in an inner method doesn't roll back the outer transaction, or vice versa. The Spring transaction management documentation covers the full propagation matrix, and knowing at least the first three propagation types (`REQUIRED`, `REQUIRES_NEW`, `NESTED`) is the threshold for a credible answer.

A short note on connection handling: `JdbcTemplate` relies on a `DataSource` for connection acquisition. In legacy apps, this was typically a JNDI-bound connection pool or a `BasicDataSource` from Apache Commons DBCP. If a candidate can explain that `JdbcTemplate` doesn't hold connections between calls — it borrows, uses, and returns within each operation — they're demonstrating the kind of operational understanding that comes from having actually debugged connection pool exhaustion in a production app.

How Verve AI Can Help You Prepare for Your Interview With Spring Framework 3

The hardest part of Spring 3 interview prep isn't memorizing the concepts — it's learning to explain them clearly under live pressure, especially when a follow-up question pushes past the definition and into the tradeoffs. That requires practice that responds to what you actually say, not a static list of sample answers.

Verve AI Interview Copilot is built for exactly that scenario. It listens in real-time to your answers and responds to what you're actually saying — including the parts you glossed over or explained unclearly. If you describe `@Transactional` without mentioning propagation, Verve AI Interview Copilot can follow up the way a real interviewer would. If your explanation of `DispatcherServlet` skips the handler mapping step, it catches that. The feedback isn't generic coaching — it's tied to the specific answer you just gave. Verve AI Interview Copilot stays invisible during the session, so you're practicing the real skill: retrieving and articulating legacy Spring knowledge under conversational pressure, not reciting it from notes.

Wrapping up

Spring Framework 3 questions aren't a test of Spring trivia. They're a test of whether you can reason clearly about an older codebase — one that mixes XML and annotations, uses lifecycle callbacks that aren't obvious, and relies on configuration patterns that Spring Boot has since automated away. The candidates who answer these questions well aren't necessarily the ones who know the most Spring. They're the ones who can explain why the legacy code was written the way it was, and what breaks when you change it carelessly.

Before your interview, focus your rehearsal on three areas: configuration (the XML-to-annotation shift, profiles, and component scanning), MVC request flow (the full path from `DispatcherServlet` to view resolution), and data access (JdbcTemplate, transaction propagation, and connection handling). These are the topics that generate follow-up questions, and follow-up questions are where the real evaluation happens. If you can talk through each of these with a specific example — not a definition, but an actual scenario — you'll sound like someone who has been inside one of these apps. That's the answer interviewers are looking for.

QO

Quinn Okafor

Interview Guidance

Ace your live interviews with AI support!

Get Started For Free

Available on Mac, Windows and iPhone