These interview questions were collected from every corner of the internet

Talk about the underlying implementation of Spring IOC

  • Reflection
  • The value of the factory
  • Design patterns
  • A few key methods
    • createBeanFactory
    • getBean
    • doGetBean
    • createBean
    • doCreateBean
    • createBeanInstance(getDeclaredConstructor, newInstance)
    • populateBean
flowchart TD
    A[Start] --> B["createBeanFactory()
create DefaultListableBeanFactory"] B --> C[Load BeanDefinitions
parse XML/annotations] C --> D["getBean(beanName)
external call entry"] D --> E["doGetBean(beanName)"] E --> F{Get singleton from cache?} F -->|Yes| G[Return the cached Bean] F -->|No| H["createBean(beanName, mbd)"] H --> I["doCreateBean(beanName, mbd)"] I --> J["createBeanInstance()
getDeclaredConstructor + newInstance"] J --> K["populateBean()
property filling / dependency injection"] K --> L["initializeBean()
initialization callbacks"] L --> M[Register destroy methods] M --> N[Return the complete Bean instance]

Talk about your understanding of Spring IOC β€” its principle and implementation

  • The IOC idea
  • DI as the means of implementation
  • What is a container, what is a Bean
  • BeanDefinition
    • Where it’s read from
      • XML
      • Annotations
    • Where it’s stored
      • All BeanDefinitions are stored in a Map inside DefaultListableBeanFactory

The container lifecycle

flowchart TD
    START([πŸš€ Program starts]) --> REFRESH["AbstractApplicationContext.refresh()"]

    REFRESH --> PHASE1

    subgraph PHASE1["πŸ”΅ Phase 1: Preparation"]
        direction TB
        A1["prepareRefresh()\nrecord start time\nset container state to 'active'\ninitialize environment variables"] --> A2
        A2["obtainFreshBeanFactory()\ncreate DefaultListableBeanFactory\nthe container's core repository"] --> A3
        A3["prepareBeanFactory()\nregister basic BeanPostProcessors\nregister Aware-related processors\nregister the default environment Bean"]
    end

    PHASE1 --> PHASE2

    subgraph PHASE2["🟒 Phase 2: Load BeanDefinitions (read the blueprints)"]
        direction TB
        B1["scan the packages specified by @ComponentScan\nor read the XML config file"] --> B2
        B2["parse annotations / XML\nrecognize @Component @Service\n@Repository @Controller @Bean"] --> B3
        B3["generate a BeanDefinition for each Bean\nrecord: class name, scope, lazy or not\ninit method, destroy method, dependencies"] --> B4
        B4[("store into beanDefinitionMap\nkey = beanName\nvalue = BeanDefinition")]
    end

    PHASE2 --> PHASE3

    subgraph PHASE3["🟑 Phase 3: Modify BeanDefinitions (edit the blueprints)"]
        direction TB
        C1["invokeBeanFactoryPostProcessors()\nrun all BeanFactoryPostProcessors"] --> C2
        C2["ConfigurationClassPostProcessor\nhandle @Configuration @Import\n@PropertySource @ComponentScan\nπŸ‘‰ Spring Boot auto-configuration triggers here"] --> C3
        C3["PropertySourcesPlaceholderConfigurer\nreplace all ${xxx} placeholders in\nBeanDefinitions with real values"] --> C4
        C4[/"all BeanDefinitions finalized\nthe blueprints no longer change"/]
    end

    PHASE3 --> PHASE4

    subgraph PHASE4["🌸 Phase 4: Register BeanPostProcessors (workers take their posts)"]
        direction TB
        D1["registerBeanPostProcessors()\nregister by priority\nPriorityOrdered β†’ Ordered β†’ ordinary"] --> D2
        D2["AutowiredAnnotationBeanPostProcessor\nhandles @Autowired @Value"] --> D3
        D3["CommonAnnotationBeanPostProcessor\nhandles @PostConstruct @PreDestroy @Resource"] --> D4
        D4["AnnotationAwareAspectJAutoProxyCreator\ndetects aspects, generates AOP proxies"] --> D5
        D5[/"all workers in place\nwaiting to intervene during Bean creation\nnot working yet"/]
    end

    PHASE4 --> PHASE5

    subgraph PHASE5["🟣 Phase 5: Initialize container infrastructure"]
        direction TB
        E1["initMessageSource()\ninitialize i18n resources"] --> E2
        E2["initApplicationEventMulticaster()\ninitialize the event multicaster"] --> E3
        E3["onRefresh()\n⭐ Spring Boot starts here\nTomcat / Jetty / Undertow\nthe web container begins listening on a port"] --> E4
        E4["registerListeners()\nregister all ApplicationListeners\nlisten for container events"]
    end

    PHASE5 --> PHASE6

    subgraph PHASE6["🟠 Phase 6: Instantiate all singleton Beans"]
        direction TB
        F1["finishBeanFactoryInitialization()\npreInstantiateSingletons()\niterate all non-lazy singleton BeanDefinitions"] --> F2
        F2["execute getBean() one by one\ntrigger each Bean's lifecycle\ninstantiate → fill properties → Aware callbacks\n→ pre-processing → init method → post-processing"] --> F3
        F3[("all singleton Beans stored in\nthe singletonObjects level-1 cache\nβœ… all ready")]
    end

    PHASE6 --> PHASE7

    subgraph PHASE7["βœ… Phase 7: Container ready"]
        direction TB
        G1["finishRefresh()\nclean up resources used at startup\ninitialize the lifecycle processor"] --> G2
        G2["publish ContextRefreshedEvent\nnotify all listeners: container startup complete"] --> G3
        G3[/"container enters running state\ncan handle business requests"/]
    end

    G3 --> RUNNING([πŸŽ‰ Container running normally])

    RUNNING -.->|"receive shutdown signal\nCtrl+C / kill / close()"| PHASE8

    subgraph PHASE8["πŸ”΄ Phase 8: Container destruction"]
        direction TB
        H1["publish ContextClosedEvent\nnotify all listeners: container about to close"] --> H2
        H2["stop the web container\nTomcat stops accepting new requests"] --> H3
        H3["run all Beans' destroy logic\n@PreDestroy β†’ destroy() β†’ destroy-method\ndestroy in reverse registration order"] --> H4
        H4["clear all caches\nsingletonObjects cleared\nbeanDefinitionMap cleared"] --> H5
        H5["set container state to 'closed'\nactive = false / closed = true"]
    end

    H5 --> END([πŸ’€ Container destruction complete])

    style START fill:#43A047,color:#fff
    style RUNNING fill:#43A047,color:#fff
    style END fill:#e53935,color:#fff
    style REFRESH fill:#1E88E5,color:#fff

The Bean lifecycle

flowchart TD
    START([🌱 Start creating the Bean]) --> A

    A["β‘  Instantiation\nInstantiation\nreflectively call the constructor\nnew an empty shell object\nall fields are null at this point"] --> B

    B["β‘‘ Put into the level-3 cache\nsingletonFactories\nexpose the half-built object early\nto prepare for circular dependencies"] --> C

    C["β‘’ Property filling\npopulateBean()\nhandle @Autowired @Value\ninject the dependent Beans"] --> D

    subgraph AWARE["β‘£ Aware callbacks"]
        direction TB
        D["BeanNameAware\ntell the Bean its own name"] --> E
        E["BeanFactoryAware\npass the BeanFactory to the Bean"] --> F
        F["ApplicationContextAware\npass the ApplicationContext to the Bean"]
    end

    subgraph INIT["β‘€ Initialization initializeBean()"]
        direction TB
        G["pre-processing\npostProcessBeforeInitialization()\nall BeanPostProcessors run one by one\nπŸ“Œ @PostConstruct is called here"] --> INITMETHOD

        subgraph INITMETHOD["init methods (executed in order)"]
            direction TB
            H1["1st: @PostConstruct\n(already run in pre-processing)"] --> H2
            H2["2nd: afterPropertiesSet()\nimplement the InitializingBean interface"] --> H3
            H3["3rd: init-method\n@Bean(initMethod='xxx') or XML config"]
        end

        INITMETHOD --> I

        I["post-processing\npostProcessAfterInitialization()\nall BeanPostProcessors run one by one\n⭐ AOP proxies are generated here"]
    end

    AWARE --> INIT

    I --> J

    subgraph CACHE["β‘₯ Store into the cache"]
        direction TB
        J["remove from the level-3 cache singletonFactories\nremove from the level-2 cache earlySingletonObjects"] --> K
        K["store into the level-1 cache singletonObjects\nβœ… complete Bean ready"]
    end

    K --> READY([πŸŽ‰ Bean is now usable])

    READY -.->|"container closes"| DESTROY

    subgraph DESTROY["⑦ Destruction phase (on container close)"]
        direction TB
        D1["1st: @PreDestroy\nmethod is called"] --> D2
        D2["2nd: destroy()\nimplement the DisposableBean interface"] --> D3
        D3["3rd: destroy-method\n@Bean(destroyMethod='xxx') or XML config"]
    end

    D3 --> END([πŸ’€ Bean destruction complete])

Spring’s three-level cache dependency flow

sequenceDiagram
    participant Spring as Spring container
    participant L3 as Level-3 cache
singletonFactories participant L2 as Level-2 cache
earlySingletonObjects participant L1 as Level-1 cache
singletonObjects Note over Spring: Start creating A Spring->>Spring: β‘  new A empty-shell object Spring->>L3: β‘‘ store A's ObjectFactory lambda Note over Spring: Injecting properties into A, discovers it needs B Spring->>Spring: β‘’ new B empty-shell object Spring->>L3: β‘£ store B's ObjectFactory lambda Note over Spring: Injecting properties into B, discovers it needs A Spring->>L1: β‘€ getBean(A), check level-1 cache L1-->>Spring: ❌ not there Spring->>L2: check level-2 cache L2-->>Spring: ❌ not there Spring->>L3: check level-3 cache L3-->>Spring: βœ… found A's lambda! Spring->>Spring: β‘₯ execute the lambda
(generate proxy A if a proxy is needed) Spring->>L2: ⑦ put the early reference into the level-2 cache Spring->>L3: remove A from the level-3 cache Note over Spring: B gets A's early reference, B finishes initialization Spring->>L1: β‘§ put B into the level-1 cache βœ… Spring->>L3: remove B from the level-3 cache Note over Spring: Back in A's flow, B is ready, A finishes initialization Spring->>L1: ⑨ put A into the level-1 cache βœ… Spring->>L2: remove A from the level-2 cache Note over L1: Finally: the level-1 cache holds complete A and B βœ…
Case Level-3 cache lambda executed Level-2 cache
No circular dep, no AOP stored the lambda ❌ not executed not used
No circular dep, with AOP stored the lambda ❌ not executed not used
Circular dep, no AOP stored the lambda βœ… executed stores the raw object
Circular dep, with AOP stored the lambda βœ… executed stores the proxy object

When Spring Bean caches are placed and removed

When Spring Bean’s three-level caches are placed and removed

Cache What it stores When placed When removed
Level-3 singletonFactories The Bean’s factory lambda Right after instantiation When the factory is called (promoted to level-2) or when the Bean completes
Level-2 earlySingletonObjects The early-exposed half-built Bean The moment the level-3 factory is called When the Bean is fully initialized and placed in level-1
Level-1 singletonObjects The complete, usable Bean After initialization fully completes When the container closes and destroys

The difference between BeanFactory and FactoryBean

What is a FactoryBean

public interface FactoryBean<T> {
    // Return the Bean instance (can involve complex creation logic)
    T getObject() throws Exception;
    
    // Return the Bean's type
    Class<?> getObjectType();
    
    // Whether it's a singleton
    default boolean isSingleton() {
        return true;
    }
}
  • Similarities
    • Both are used to create Bean objects
  • Differences
    • Creating an object with BeanFactory must follow the strict lifecycle process, which is too complex. If you want to simply customize the creation of some object while still handing it to Spring to manage, you must implement the FactoryBean interface
      • isSingleton β€” whether it’s a singleton
      • getObjectType β€” get the returned object’s type
      • getObject β€” customize the object-creation process
Dimension BeanFactory FactoryBean
Role Container/factory interface A special Bean
Function Manages the lifecycle of all Beans Customizes the creation of a specific Bean
Positioning Infrastructure (the IOC container) Business extension (creating complex objects)
How it’s used Implemented and used by the Spring framework Implemented by the developer, registered into the container
Common implementations DefaultListableBeanFactory SqlSessionFactoryBean, ProxyFactoryBean
Who creates whom BeanFactory creates and manages FactoryBean FactoryBean creates business objects

flowchart LR
    subgraph BeanFactory[BeanFactory - IOC container]
        direction LR
        Bean1[ordinary Bean]
        Bean2[ordinary Bean]
        FB[FactoryBean
special Bean] end FB -->|call getObject| Result[business object] User[developer] -->|getBean| BF[BeanFactory] BF -->|return| Bean1 BF -->|return| Bean2 BF -->|return getObject result| Result BF -->|add & prefix| FB

Design patterns used in Spring

  • Singleton
  • Prototype (scope set to prototype)
  • Factory
    • BeanFactory
  • Template Method
    • JdbcTemplate
    • TransactionTemplate
    • RestTemplate
    • RedisTemplate
  • Strategy
    • XmlBeanDefinitionReader
    • PropertiesBeanDefinitionReader
  • Observer
    • listener
    • event
    • multicast
  • Adapter
    • HandlerAdapter
  • Decorator
    • BeanWrapper
  • Chain of Responsibility
    • When using AOP, an interceptor chain is generated first
    • The filter chain of Spring MVC
  • Proxy
    • Dynamic proxy
  • Delegate
    • delegate

The underlying implementation principle of Spring AOP

πŸ₯‡ Level 1: characterize it in one sentence (opening)

“The underlying essence of Spring AOP is dynamic proxy. When the container initializes a Bean, Spring intercepts via the BeanPostProcessor mechanism, decides whether the Bean needs enhancement, and if so, uses a dynamic proxy to generate a proxy object that replaces the original Bean when registered into the container. All subsequent calls to this Bean actually go through the proxy object.”


πŸ₯ˆ Level 2: expand on the two proxy approaches (core)

“There are two concrete proxy approachesβ€””

“The first is JDK dynamic proxy, based on interfaces, using Proxy.newProxyInstance to generate the proxy; the target class must have an interface.”

“The second is CGLIB dynamic proxy, which generates a subclass of the target class at runtime via bytecode, needs no interface, but the target class and methods can’t be final.”

“Since Spring Boot 2.x, CGLIB is forced by default unless you manually configure proxyTargetClass = false.”


πŸ₯‰ Level 3: explain the call chain clearly (highlight)

“On the call chain, Spring assembles all matching Advice into an interceptor chain via ReflectiveMethodInvocation, executing them in turn through recursive proceed(). The order is: @Around first half β†’ @Before β†’ target method β†’ @AfterReturning / @AfterThrowing β†’ @After (finally) β†’ @Around second half.”


πŸ’‘ Proactively raise a pitfall to stand out

“There’s a common pitfall here β€” self-invocation within the same class breaks AOP. For example, method A calls same-class method B via this.B(), going through the original object and bypassing the proxy, so annotations like @Transactional won’t take effect. The fix is to inject the class’s own proxy object, or use AopContext.currentProxy() to get the current proxy.”


Prepared follow-ups

Interviewer follow-up Direction of your response
JDK vs CGLIB performance difference? JDK reflective calls were slow early on, JDK 8+ optimized them; CGLIB is slow to create but fast to call; modern versions differ little
What’s the difference between Spring AOP and AspectJ? Spring AOP is runtime proxy, can only intercept methods of Spring-managed Beans; AspectJ weaves bytecode at compile/load time, more powerful (can intercept constructors, fields)
Why is @Transactional based on AOP? It’s essentially an Around Advice β€” open the transaction before the method, commit on normal end, roll back on exception
When is BeanPostProcessor triggered? After Bean initialization completes (the last stage of initializeBean), postProcessAfterInitialization is called
Why can’t a final method be proxied by CGLIB? CGLIB overrides methods by generating a subclass through inheritance; a final method can’t be overridden by a subclass, so it can’t be intercepted

❌ Common answer mistakes

❌ "Spring AOP just uses reflection"
   β†’ too shallow; reflection is only how the JDK proxy calls the target method

❌ "CGLIB is faster than the JDK proxy, so Spring uses CGLIB by default"
   β†’ logic reversed; the reason Spring Boot 2.x changed the default was mainly to
     avoid type-cast issues when "an interface proxy is injected into an implementation class," not pure performance

❌ Explaining AspectJ's compile-time weaving as the principle of Spring AOP
   β†’ conflating two systems; Spring AOP doesn't use AspectJ's weaver by default

The Spring MVC adapter request-handling flow

flowchart TD
    A([HTTP request comes in]) --> B

    B["DispatcherServlet\n.doDispatch()"]

    B --> C["HandlerMapping\n.getHandler()\n───────────\nfind the matching Handler by URL\nreturn HandlerExecutionChain"]

    C --> D{"What type is the Handler?"}

    D -->|"implements the Controller interface\n(old style)"| E1["SimpleControllerHandlerAdapter\n.supports(handler) β†’ true"]
    D -->|"@RequestMapping-annotated method\n(modern style)"| E2["RequestMappingHandlerAdapter\n.supports(handler) β†’ true"]
    D -->|"implements HttpRequestHandler\n(static resources, etc.)"| E3["HttpRequestHandlerAdapter\n.supports(handler) β†’ true"]

    E1 --> F1["SimpleControllerHandlerAdapter\n.handle()\n───────────\ninternally casts and calls:\n((Controller) handler)\n.handleRequest(req, res)"]

    E2 --> F2["RequestMappingHandlerAdapter\n.handle()\n───────────\ninternally invokes via reflection:\nhandlerMethod.getMethod()\n.invoke(bean, args)"]

    E3 --> F3["HttpRequestHandlerAdapter\n.handle()\n───────────\ninternally casts and calls:\n((HttpRequestHandler) handler)\n.handleRequest(req, res)"]

    F1 --> G["return ModelAndView\nto the DispatcherServlet"]
    F2 --> G
    F3 --> G

    G --> H{"Is there a view?"}
    H -->|"has a view name\ntraditional page"| I["ViewResolver\n.resolveViewName()\n───────────\nresolve into a View object\nrender HTML and return"]
    H -->|"@ResponseBody\nREST API"| J["MessageConverter\n.write()\n───────────\nserialize the object to JSON\nwrite directly to the response body"]

    style B fill:#f4a261,color:#000
    style E1 fill:#457b9d,color:#fff
    style E2 fill:#457b9d,color:#fff
    style E3 fill:#457b9d,color:#fff
    style F1 fill:#2a9d8f,color:#fff
    style F2 fill:#2a9d8f,color:#fff
    style F3 fill:#2a9d8f,color:#fff

The complete Spring MVC request-handling flow

sequenceDiagram
    actor User browser
    participant DS as DispatcherServlet
(front-desk receptionist) participant HM as HandlerMapping
(route finder) participant HI as HandlerInterceptor
(interceptor) participant HA as HandlerAdapter
(adapter) participant HC as Handler/Controller
(does the real work) participant MAR as MessageConverter
(data converter) participant VR as ViewResolver
(view resolver) participant V as View
(template/page) User browser->>DS: β‘  HTTP request (GET /home) DS->>HM: β‘‘ I received the request, who handles it? HM-->>DS: β‘’ return HandlerExecutionChain
(Handler + interceptor list) DS->>HI: β‘£ preHandle()
pre-interception (login check, logging, etc.) alt interceptor returns false HI-->>User browser: intercept and return directly (e.g. redirect to login) end DS->>HA: β‘€ find the adapter that can handle this Handler
(matched by supports()) HA->>HC: β‘₯ call the Handler
(handle() unified entry) note over HC: run business logic
call Service / DAO HC-->>HA: ⑦ return the result
(ModelAndView or @ResponseBody data) HA-->>DS: β‘§ return ModelAndView DS->>HI: ⑨ postHandle()
post-interception (can modify ModelAndView) alt returns @ResponseBody (REST API) DS->>MAR: β‘© use MessageConverter to serialize
the object to JSON/XML MAR-->>User browser: β‘ͺ write directly to the response body and return else returns a view name (traditional MVC) DS->>VR: β‘© resolve the view name β†’ find the template file VR-->>DS: β‘ͺ return the View object DS->>V: β‘« render the view (fill in the Model data) V-->>User browser: ⑬ return the HTML page end DS->>HI: β‘­ afterCompletion()
final callback (resource cleanup, exception logging)

How does Spring roll back a transaction

πŸ₯‡ Level 1: characterize it in one sentence (opening)

“Spring’s transaction rollback is implemented based on AOP. @Transactional is essentially an Around Advice: Spring opens the transaction before the method executes, commits if the method returns normally, and triggers a rollback if it catches an exception. The concrete transaction operations are delegated to the PlatformTransactionManager, which interacts with the underlying database.”


πŸ₯ˆ Level 2: explain the full flow clearly (core)

“The concrete flow is like thisβ€””

flowchart TD
    A["call a @Transactional method"] --> B

    subgraph TI ["TransactionInterceptor"]
        B["invoke(MethodInvocation invocation)"]
    end

    B --> C

    subgraph TAS ["TransactionAspectSupport"]
        C["invokeWithinTransaction()"]
        C --> D["createTransactionIfNecessary()\nget or create the transaction"]
        D --> E["invocation.proceedWithInvocation()\nexecute the target method"]
        E --> F{result}
        F -->|normal return| G["commitTransactionAfterReturning()"]
        F -->|exception thrown| H["completeTransactionAfterThrowing()\ncheck whether it matches the rollback rules"]
        H -->|matches rollback rules| I["rollbackOnException()\n→ rollback()"]
        H -->|doesn't match rollback rules| J["commit()"]
        E --> K["finally:\ncleanupTransactionInfo()"]
    end

    subgraph APTM ["AbstractPlatformTransactionManager\n(DataSourceTransactionManager implementation)"]
        D --> L["doBegin()\nconnection.setAutoCommit(false)"]
        G --> M["doCommit()"]
        I --> N["doRollback()"]
        J --> M
    end

    style B fill:#f39c12,color:#fff
    style C fill:#f39c12,color:#fff
    style D fill:#8e44ad,color:#fff
    style L fill:#8e44ad,color:#fff
    style E fill:#2ecc71,color:#000
    style G fill:#27ae60,color:#fff
    style M fill:#27ae60,color:#fff
    style H fill:#c0392b,color:#fff
    style I fill:#e74c3c,color:#fff
    style N fill:#e74c3c,color:#fff
    style J fill:#27ae60,color:#fff
    style K fill:#7f8c8d,color:#fff

“For deciding whether to roll back, Spring by default only rolls back on RuntimeException and Error; checked exceptions do not roll back by default.”


πŸ₯‰ Level 3: explain the rollback-rule configuration (details)

“The rollback rules can be configured manuallyβ€””

// Specify that a certain checked exception should also roll back
@Transactional(rollbackFor = Exception.class)

// Specify that a certain exception should not roll back
@Transactional(noRollbackFor = IllegalArgumentException.class)

“Internally Spring uses RollbackRuleAttribute to match the exception type, traversing the exception inheritance chain to find the nearest matching rule to decide whether to commit or roll back.”


πŸ’‘ Proactively raise classic pitfalls to stand out

Pitfall 1: self-invocation breaks the transaction (same root cause as AOP)

“When same-class methods call each other, @Transactional doesn’t take effect β€” the same reason as AOP self-invocation failure: it bypasses the proxy object.”

@Service
public class OrderService {
    public void placeOrder() {
        this.pay(); // ❌ transaction doesn't take effect, goes through the original object
    }

    @Transactional
    public void pay() { ... }
}

Pitfall 2: the exception is swallowed, so the transaction can’t perceive it

“If you try-catch and swallow the exception inside the method, the TransactionInterceptor can’t catch it and won’t trigger a rollback.”

@Transactional
public void pay() {
    try {
        db.update(...);
    } catch (Exception e) {
        log.error("error", e); // ❌ exception swallowed, transaction commits as usual
    }
}

“The fix: mark for rollback manually after catchingβ€””

catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus()
        .setRollbackOnly(); // βœ… trigger rollback manually
}

Pitfall 3: checked exceptions don’t roll back by default

@Transactional
public void pay() throws IOException {
    throw new IOException("file not found"); // ❌ doesn't roll back by default!
}

// The correct way:
@Transactional(rollbackFor = Exception.class) // βœ…

Prepared follow-ups

Interviewer follow-up Direction of your response
Explain transaction propagation REQUIRED (default, join or create new) / REQUIRES_NEW (suspend the outer, create new) / NESTED (nested, savepoint), etc. β€” 7 in total, focus on the first three
Difference between REQUIRES_NEW and NESTED? REQUIRES_NEW is a fully independent new transaction, unaffected by the outer’s rollback; NESTED is nested within the outer transaction, and the outer’s rollback drags it along
Relationship between Spring transactions and DB transactions? A Spring transaction is management wrapping the DB connection; it ultimately relies on the DB’s ACID, Spring only controls the timing of commit/rollback
Does @Transactional still work in multithreading? No. Spring binds the current thread’s connection via ThreadLocal; a child thread can’t get the same connection, so the transaction can’t propagate
Does @Transactional on an interface work? Not recommended. Marginally works under JDK proxy, completely fails under CGLIB proxy; Spring officially recommends always putting it on the implementation class

❌ Common answer mistakes

❌ "A Spring transaction is just an annotation that auto-commits and rolls back"
   β†’ doesn't mention the core mechanisms: AOP proxy, TransactionInterceptor, connection management

❌ "All exceptions roll back"
   β†’ a classic mistake; by default only RuntimeException and Error roll back

❌ "I only know REQUIRED for propagation"
   β†’ you should at least be able to name REQUIRES_NEW and NESTED and their differences

Talk about Spring’s transaction propagation

1. Core must-knows (most common, decide life and death)

  • REQUIRED (default behavior)

  • In one sentence: live and die together.

  • Logic: if the outer has a transaction, join it; if not, create a new one itself. If any one errors, the whole thing rolls back.

  • REQUIRES_NEW

  • In one sentence: each goes its own way.

  • Logic: regardless of whether the outer has a transaction, it must suspend the outer and open a brand-new independent transaction. The two don’t interfere with each other β€” good for logging.

  • NESTED

  • In one sentence: elder and younger in order.

  • Logic: establish a “savepoint” within the outer transaction. If the sub-transaction fails, it can roll back alone without affecting the outer; but if the outer rolls back, the sub-transaction must roll back along with it.


2. Gentle compliance (adapt to the outer environment)

  • SUPPORTS

  • In one sentence: go with the flow.

  • Logic: if the outer has a transaction, join and run in it; if the outer has none, run non-transactionally (as an ordinary method).

  • NOT_SUPPORTED

  • In one sentence: refuse to get pulled in.

  • Logic: doesn’t support transactions. If the outer has a transaction, first suspend/pause the outer, run itself non-transactionally, then let the outer transaction continue.


3. Hard exclusion (the extreme rule-breakers)

  • MANDATORY

  • In one sentence: no ticket, no entry.

  • Logic: mandatorily requires the outer to have a transaction; if not, throw an exception directly (IllegalTransactionStateException).

  • NEVER

  • In one sentence: never touch the poison.

  • Logic: firmly does not support transactions. If the outer has a transaction, throw an exception directly; only when the outer has none will it run normally.