Comprehensive Spring and SpringBoot Interview Questions and Answers

Table of Contents

  1. Spring Framework Overview
  2. IOC and DI
  3. Spring Bean Configuration
  4. Annotations
  5. Aspect-Oriented Programming (AOP)
  6. JdbcTemplate
  7. Transaction Management
  8. SpringBoot Configuration
  9. Circular Dependency Issues
  10. Additional Topics

1. Spring Framework Overview

1.1 What is Spring?

Spring's core concept is Inversion of Control (IOC), where Spring manages object lifecycle and relationships between objects. Previously, developers manually instantiated objects using new in their classes. With Spring, this responsibility is delegated to the framework.

In traditional development, when one class defines an instance of another class, they become tightly coupled. Spring's approach keeps classes independent—they are injected when needed, promoting loose coupling.

1.2 What are the advantages of Spring?

Spring provides four major benefits:

  • Decoupling: IOC enables loose coupling between components
  • Aspect-Oriented Programming: Separates cross-cutting concerns
  • Framework Integration: Easily integrates with various open-source frameworks
  • Comprehensive Solution: Provides a full-stack framework including SpringMVC for presentation and Spring JDBC for persistence

2. IOC and DI

2.1 What are the different overloads of the getBean method?

There are three ways to retrieve beans from the Spring container:

// Approach 1: Retrieve by bean identifier
UserAccount user1 = (UserAccount) applicationContext.getBean("userAccount");

// Approach 2: Retrieve by type
UserAccount user2 = applicationContext.getBean(UserAccount.class);

// Approach 3: Retrieve by identifier and type
UserAccount user3 = applicationContext.getBean("userAccount", UserAccount.class);

2.2 What are the dependency injection methods in Spring?

Spring supports three DI approaches:

  1. Setter injection
  2. Constructor injection
  3. Field injection

2.3 Why does Spring discourage field injection?

Field injection presents several issues:

Single Responsibility Violation

Following SOLID principles, each class should have one responsibility. With field injection, as business requirements grow, fields accumulate unnoticed. Constructor injection makes this violation more apparent due to its verbosity, signaling when refactoring is needed.

Potential NullPointerException

Bean initialization order: static fields → instance fields → constructor → @Autowired fields. Using @Autowired in constructors or static blocks causes NPE:

@Component
class PaymentProcessor {
    @Autowired
    private PaymentGateway gateway;

    private final String processorName;

    public PaymentProcessor() {
        // gateway is not initialized yet - will throw NPE
        this.processorName = gateway.getProcessorName();
    }
}

Constructor injection ensures the dependency is available when the object is fully constructed.

Hidden Dependencies

Proper DI makes dependencies explicit through constructors or public setters. Field injection exposes private fields to the container, breaking encapsulation. The container shouldn't need to access bean internals.

Testing Difficulties

Field injection with @Autowired requires a Spring container for unit testing, making tests heavy and slow. Constructor injection or @Resource annotations facilitate easier testing.

2.4 What is the relationship between BeanFactory and FactoryBean?

Both are interfaces in Spring's container hierarchy.

BeanFactory serves as the fundamental IoC container interface, managing bean creation and lifecycle. It's the core interface that applications can use to interact with the container.

FactoryBean defines a factory for creating specific object types. When a bean implements FactoryBean in the configuration, Spring doesn't return the FactoryBean itself but calls getObject() to return the created object.

FactoryBean is ideal for creating complex objects requiring specific initialization, such as JNDI resources or proxy objects. A practical example is Dubbo's ReferenceBean:

ReferenceBean implements FactoryBean and overrides getObject(). This method creates a dynamic proxy for remote service calls, abstracting network communication details. The proxy makes remote method invocations appear as local calls.

This pattern enables lazy proxy creation until actual usage, improving startup time and resource consumption. It also supports sophisticated loading strategies and optimization while leveraging Spring's dependency injection and lifecycle management.


3. Spring Bean Configuration

3.1 What are the ways to create objects in Spring?

Spring supports three object creation mechanisms:

  1. Default constructor (no-arg constructor)
  2. Static factory method
  3. Instance factory method

3.2 What are the bean scopes?

Spring provides four bean scopes:

  • singleton: Default scope, one instance per container
  • prototype: New instance for each request
  • request: One instance per HTTP request
  • session: One instance per HTTP session

3.3 What is the bean lifecycle?

The Spring bean lifecycle involves these stages:

1. Bean Instantiation

Spring creates the bean instance through createBeanInstance() in AbstractAutowireCapableBeanFactory.

2. Property Population

Spring injects necessary properties via populateBean() in AbstractAutowireCapableBeanFactory.

3. Aware Interfaces

If beans implement aware interfaces like BeanNameAware or BeanClassLoaderAware, Spring invokes them during initializeBean().

4. BeanPostProcessor Pre-Processing

Custom BeanPostProcessor implementations can modify bean state before initialization through postProcessBeforeInitialization().

5. InitializingBean.afterPropertiesSet()

Beans implementing InitializingBean have this method called after properties are set.

6. Custom init-method

XML-defined or annotation-specified initialization methods execute during invokeInitMethods().

7. BeanPostProcessor Post-Processing

postProcessAfterInitialization() runs after initialization completes.

8. Destruction Callback Registration

Beans implementing DisposableBean or defining destroy methods get destruction callbacks registered.

9. Bean Ready for Use

The bean is fully initialized and ready to handle application requests.

10. DisposableBean.destroy()

Called during container shutdown for beans implementing DisposableBean.

11. Custom destroy-method

Configured destroy methods execute during shutdown.

Bean creation relies on AbstractAutowireCapableBeanFactory, while destruction uses DisposableBeanAdapter.

3.4 Are beans thread-safe?

Thread safety depends on bean scope. Spring provides multiple scopes, with singleton and prototype being most common.

singleton is the default scope—only one instance exists in the container. This shared instance can pose thread safety risks when the bean contains shared mutable state.

prototype scope creates a new instance for each retrieval. Since instances aren't shared across threads, there's no thread safety concern.

The actual thread safety depends on whether the singleton bean contains shared variables.

3.5 What is ApplicationContext and when would you use it?

ApplicationContext extends BeanFactory, enabling bean retrieval within Spring's context. Typically, Spring manages bean injection automatically using @Service and @Autowired:

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
}

This requires both beans to be Spring-managed. However, non-Spring-managed classes (like domain models using the rich domain model pattern) may need to access beans:

public class CaseEntity {
    CollectionCaseItemService service = SpringContextHolder.getBean(CollectionCaseItemService.class);
}

Implementation:

@Component
public class SpringContextHolder implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringContextHolder.applicationContext = context;
    }

    public static Object getBean(String name) throws BeansException {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> type) throws BeansException {
        return applicationContext.getBean(type);
    }
}

3.6 How to make a bean load before other beans in SpringBoot?

Direct Dependency

@Component
public class AnalyticsService {
    @Autowired
    private DataSource dataSource;
}

Loading AnalyticsService ensures DataSource initializes first.

@DependsOn Annotation

For external library beans that can't be modified:

@Configuration
public class BeanOrderConfig {
    @Bean
    @DependsOn("dataSource")
    public ConnectionPool pool() {
        return new ConnectionPool();
    }
}

@DependsOn also works with @Component.

BeanFactoryPostProcessor

For initialization before all other beans:

@Component
public class PriorityProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        PriorityBean bean = factory.getBean(PriorityBean.class);
        System.out.println(bean);
    }
}

@Component
public class PriorityBean {
    public PriorityBean() {
        System.out.println("Initializing priority bean");
    }
}

3.7 Can @Order solve bean initialization ordering?

@Order controls ordering within a single bean type collection, not between different beans:

@Component
public class HandlerRegistry {
    private final List<Handler> handlers;

    public HandlerRegistry(List<Handler> handlers) {
        this.handlers = handlers;
    }
}

@Order(1)
@Component
class LoggingHandler implements Handler {}

@Order(2)
@Component
class ValidationHandler implements Handler {}

In this case, LoggingHandler appears before ValidationHandler in the list.


4. Annotations

4.1 What Spring annotations have you used?

  • @Controller: Presentation layer
  • @Service: Business layer
  • @Repository: Data access layer
  • @Component: General-purpose stereotype
  • @Resource: Name-based injection (Java annotation)
  • @Autowired: Type-based injection (Spring annotation)
  • @Scope: Bean scope configuration
  • @Aspect, @Before, @After, @Around, @Pointcut: AOP support

4.2 What is the difference between @Controller, @Service, @Repository, and @Component?

Functionally, they're identical—all serve as stereotype annotations that register beans in Spring's context. The differences are semantic:

  • @Controller indicates web layer components
  • @Service indicates business logic components
  • @Repository indicates data access components
  • @Component indicates general components

Spring won't enforce these conventions, but they improve code readability and convey intent.

4.3 What is the difference between @Resource and @Autowired?

Origin

  • @Autowired: Spring annotation
  • @Resource: Java annotation (JSR-250)

Resolution Order

  • @Autowired: Type-first, then name-based resolution
  • @Resource: Name-first, then type-based resolution

5. Aspect-Oriented Programming (AOP)

5.1 What is AOP?

AOP extracts cross-cutting concerns into separate modules, allowing application objects to focus solely on business logic. Other concerns (logging, transactions, security) are handled by other objects.

Example: A celebrity focuses on performing while an agent handles contracts, venues, and negotiations.

5.2 How to implement AOP manually?

Two approaches exist:

JDK Dynamic Proxy: Requires interfaces. Creates proxy implementations at runtime.

CGLIB: Works with classes without interfaces. Uses bytecode enhancement to create subclasses at runtime.

5.3 Have you used AspectJ in projects?

AspectJ is a Java-based AOP framework. Spring 2.0+ supports AspectJ pointcut expressions.

@AspectJ enables AOP using JDK 5 annotations, allowing aspect definitions directly in bean classes. Modern Spring recommends AspectJ-style AOP development.

5.4 What are the advice types?

Five advice types exist:

  • @Before: Executes before method. Blocks method execution if exception is thrown.
  • @AfterReturning: Executes after normal method completion. Can access return value since it runs after method execution.
  • @Around: Most powerful. Executes before and after method. Can prevent method execution entirely. Requires manual invocation of proceed().
  • @AfterThrowing: Executes when method throws exception. Used for exception wrapping.
  • @After: Executes after method completion (finally semantics). Runs regardless of exception.

5.5 When does Spring AOP fail?

AOP doesn't work in these scenarios:

  1. Private method invocations
  2. Static method calls
  3. Final method calls
  4. Self-invocations within the same class
  5. Internal class method calls

5.6 How to use Spring Event for event-driven architecture?

Spring Event implements the observer pattern, enabling component communication through events.

Three components needed:

  1. Event: Plain Java object containing event data
  2. EventPublisher: Triggers events and notifies listeners
  3. EventListener: Responds to specific event types

6. JdbcTemplate

6.1 Have you used JdbcTemplate? Name some common methods.

Common JdbcTemplate methods:

  • Insert/Update/Delete: update() method
  • Batch operations: batchUpdate() method
  • Single entity query: queryForObject() method
  • List query: query() method
  • Map query: queryForMap() method
  • List of Maps: queryForList() method

7. Transaction Management

7.1 What transaction management types does Spring support?

Spring supports:

  • Programmatic Transaction Management: Manual transaction control through JDBC API
  • Declarative Transaction Management: Annotation-based (@Transactional)

Programmatic approach involves: acquiring connection, disabling auto-commit, executing operations, committing or rolling back, closing resources.

7.2 How do you implement transactions in projects?

Using @Transactional annotation on classes or methods.

7.3 What attributes does @Transactional have?

Four commonly used attributes:

  • propagation: Transaction propagation behavior
  • isolation: Transaction isolation level
  • timeout: Transaction timeout in seconds
  • readOnly: Whether transaction is read-only

7.4 What is transaction propagation?

When method A with existing transaction calls method B, transaction propagation defines how B handles that transaction.

7.5 How many propagation behaviors exist?

Seven propagation behaviors:

  • REQUIRED (default)
  • SUPPORTS
  • MANDATORY
  • REQUIRES_NEW
  • NOT_SUPPORTED
  • NEVER
  • NESTED

7.6 How many isolation levels exist?

Four standard isolation levels:

  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

7.7 How do you understand the timeout attribute?

Consider a flash sale with 10,000 concurrent requests. If 1,000 requests get stuck at a certain step without timeout, connections remain held indefinitely. Setting timeout=5 forces rollback after 5 seconds, releasing connections.

7.8 How do you understand and use readOnly?

Setting readOnly=true instructs databases to avoid locks during read operations, improving performance.

Question: Can readOnly="true" be used with write operations?

It can be set but is counterproductive. With readOnly=true and writes, no locks are applied, causing dirty reads, phantom reads, and non-repeatable reads. readOnly=true should only be used when all operations are reads.

7.9 What causes Spring transactions to fail?

1. Proxy Failure

@Transactional relies on Spring AOP (dynamic proxy). When proxy creation fails, transactions won't work.

2. Incorrect @Transactional Usage

Wrong propagation settings:

@Service
public class OrderService {
    @Autowired
    private OrderRepository repository;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void processOrder() {
        repository.updateInventory();
    }
}

@Service
public class CheckoutService {
    @Autowired
    private OrderService orderService;

    @Transactional
    public void checkout() {
        orderService.processOrder();
        repository.updatePayment();
    }
}

If checkout rolls back, processOrder won't roll back because NOT_SUPPORTED means no transaction.

3. Exception Caught

@Service
public class PaymentService {
    @Transactional
    public void processPayment() {
        try {
            executePayment();
        } catch (Exception e) {
            log.error(e);
        }
    }
}

Catching exception prevents rollback—transaction commits.

4. Multi-threading in Transactions

@Transactional uses ThreadLocal for transaction context. ThreadLocal is thread-isolated. New threads won't participate in the original transaction.

5. Non-transactional Database Engine

MyISAM doesn't support transactions.

7.10 Do transactions work with @Transactional and @Async together?

Scenario 1: Same method with both annotations

@Service
public class UserRegistrationService {
    @Transactional
    @Async
    public void register(String phone, String code) {
        userDao.save(user);
        notificationDao.send(user, NotificationType.REGISTER);
        throw new RuntimeException("force rollback");
    }
}

Transaction works—RuntimeException triggers rollback for both tables.

Scenario 2: @Transactional method calling @Async method

@Service
public class RegistrationService {
    @Autowired
    private NotificationService notificationService;

    @Transactional
    public void register(String phone, String code) {
        userDao.save(user);
        notificationService.sendAsync(user);
        throw new RuntimeException("rollback");
    }
}

@Service
public class NotificationService {
    @Async
    public void sendAsync(User user) {
        notificationDao.send(user);
    }
}

A's exception rolls back A. B runs in new thread without A's transaction—B won't roll back with A.

Scenario 3: @Async method calling @Transactional method

@Service
public class RegistrationService {
    @Autowired
    private NotificationService notificationService;

    @Async
    public void register(String phone, String code) {
        userDao.save(user);
        notificationService.sendWithTransaction(user);
    }
}

@Service
public class NotificationService {
    @Transactional
    public void sendWithTransaction(User user) {
        notificationDao.send(user);
        throw new RuntimeException("error");
    }
}

A's exception doesn't affect B's transaction. B's exception rolls back B only.


8. SpringBoot Configuration

8.1 How does SpringBoot achieve auto-configuration?

SpringBoot detects classes on classpath and automatically configures beans. This dramatically reduces manual configuration.

SpringBoot uses conditional configuration to determine which beans can be configured. These conditions are defined as Configuration classes, registered in spring.factories (deprecated in 2.7.0) or META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (current approach).

The key is org.springframework.boot.autoconfigure.EnableAutoConfiguration.

When the container starts with @EnableAutoConfiguration, the imported EnableAutoConfigurationImportSelector scans spring.factories files and performs auto-configuration.


9. Circular Dependency

9.1 What is Spring circular dependency?

Circular dependency occurs when two or more beans depend on eachother directly or indirectly. Without handling, this causes application startup failure:

@Service
public class AccountService {
    @Autowired
    private PaymentService paymentService;
}

@Service
public class PaymentService {
    @Autowired
    private AccountService accountService;
}

9.2 How to resolve constructor injection circular dependency?

Option 1: Refactor to eliminate circular dependency

Often indicates poor design. Refactoring may be necessary.

Option 2: Use non-constructor injection

Switch to setter or field injection.

Option 3: Use @Lazy annotation

Delays bean creation until needed.

9.3 What is Spring's three-level cache?

DefaultSingletonBeanRegistry maintains three caches:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry {
    // Level 1: Complete singleton objects ready for use
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // Level 3: Object factories for singleton creation
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    // Level 2: Early singleton references (not fully initialized)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
}
  • singletonObjects (L1): Fully initialized beans
  • earlySingletonObjects (L2): Partially initialized beans during circular dependency handling
  • singletonFactories (L3): Object factories for lazy proxy creation

9.4 How does the three-level cache solve circular dependency?

Spring bean creation divides into two phases: instantiation (memory allocation, constructor) and initialization (property population).

Initialization can be deferred—when creating ServiceA, Spring instantiates it first, then initializes its ServiceB property.

Why three levels?

Level 2 alone can hold beans after property population but before full initialization—other beans might reference incomplete beans. Level 3 provides factories immediately after instantiation, allowing references to "early" objects without affecting ongoing initialization.

Example: A and B depend on each other.

  1. Creating A finds dependency on B
  2. Creating B finds dependency on A
  3. Level 3 factory creates A's early reference, stored in Level 2
  4. B gets early A from Level 2, completes injection
  5. B completes initialization, moves to Level 1
  6. A completes initialization, moves from Level 2 to Level 1

9.5 Is three-level cache necessary for circular dependency?

Two-level cache could work but has issues. Full reliance on Level 2 means creating AOP proxies immediately after instantiation, before the initialization lifecycle completes.

Spring's design uses AnnotationAwareAspectJAutoProxyCreator for AOP in the final lifecycle stage, unaware which beans will have circular dependencies.

Option 1: Always create proxies early (simpler, but violates Spring's design principle)

Option 2: Create proxies on-demand when circular dependency occurs (maintains lifecycle integrity)

Spring chooses Option 2 with Level 3 factories—creating proxies on-demand while preserving AOP design principles.

9.6 Can @Lazy resolve circular dependency?

Yes. Three-level cache can't resolve constructor injection circular dependencies, but @Lazy can. With @Lazy, Spring delays bean creation until needed—breaking the cycle by deferring one bean's initialization.

9.7 Does Spring support circular dependency by default? How to handle it?

Before SpringBoot 2.6: enabled by default

SpringBoot 2.6+: disabled by default

Even though Spring's three-level cache handles circular dependencies, Spring considers them poor design and disables support by default.

Enabling circular dependency support:

  1. Configuration: spring.main.allow-circular-references=true
  2. Annotation: Add @Lazy to @Autowired

10. Additional Topics

10.1 What are the new features in Spring 6.0 and SpringBoot 3.0?

AOT Compilation

Ahead-Of-Time (AOT) compiles before runtime, unlike Just-In-Time (JIT). Benefits:

  • No runtime compilation overhead
  • Faster startup
  • Lower memory consumption

AOT addresses Spring's historical issues: slow startup, high memory usage, and GC pressure.

Spring Native

Spring Native enables compiling Spring applicasions to native executables using GraalVM, eliminating JVM dependency.

Benefits:

  • Standalone executable (no JVM needed)
  • Extremely fast startup
  • Lower resource consumption

Drawback: Longer build times compared to JVM-based builds.

10.2 What is the purpose of shutdown hook in Spring?

Shutdown hooks execute cleanup during application termination. Spring registers hooks with the JVM to perform:

  • Bean destruction
  • Container shutdown
  • Resource cleanup

AbstractApplicationContext provides registerShutdownHook() for hook registration. Many middleware systems implement graceful shutdown using Spring's shutdown hook mechanism (e.g., Dubbo's graceful shutdown).

10.3 Why not use @Async directly?

Without a custom thread pool, @Async uses SimpleAsyncTaskExecutor, which isn't a true thread pool:

  • Doesn't reuse threads
  • Creates new thread per task
  • No maximum thread limit
  • Causes severe performance issues under high concurrency

Always define a custom thread pool for @Async.

10.4 What design patterns does Spring use?

Factory Pattern

IOC container acts as a factory—configure via annotations/XML without manual object creation. Handles lifecycle management.

Composite Pattern

Extensively used in SpringMVC for parameter resolution and response handling. HandlerMethodArgumentResolver uses composite pattern—parent implements the interface, aggregates child implementations. Also applies strategy pattern via supportsParameter() method.

Adapter Pattern

Adapts different interfaces for compatibility. HandlerAdapter in SpringMVC is a classic example.

Proxy Pattern

AOP uses proxy to enhance target classes (logging, transactions). Differs from adapter—adapter changes interface, proxy enhances behavior.

Singleton Pattern

Spring beans are singleton by default, maximizing reuse and thread safety.

10.5 What is MVC?

  • Model: Business logic and data structures. Handles data input, output, updates, and storage. Doesn't care about presentation or user interaction.

  • View: User interface displaying model data. Contains controls and elements for data presentation and user interaction. Doesn't handle data processing.

  • Controller: Application logic controlling interaction between view and model. Handles events and triggers, responding to user input or view changes. Converts user input to model operations, decoupling view from model.

MVC separates representation from processing, making applications flexible, maintainable, and extensible. Improves readability, maintainability, and enables code reuse and team collaboration.


Tags

["Spring", "SpringBoot", "IOC", "DI", "AOP", "Transaction Management", "Circular Dependency", "Interview Questions", "Java", "Backend Development"]

Tags: Spring SpringBoot IoC DI aop

Posted on Sat, 15 Aug 2026 16:20:21 +0000 by lucianoes