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
- Where it’s read from
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
- 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
| 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
BeanPostProcessormechanism, 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.newProxyInstanceto 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
Adviceinto an interceptor chain viaReflectiveMethodInvocation, executing them in turn through recursiveproceed(). The order is:@Aroundfirst half β@Beforeβ target method β@AfterReturning/@AfterThrowingβ@After(finally) β@Aroundsecond 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@Transactionalwon’t take effect. The fix is to inject the class’s own proxy object, or useAopContext.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.
@Transactionalis essentially anAround 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 thePlatformTransactionManager, 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
RuntimeExceptionandError; 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
RollbackRuleAttributeto 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,
@Transactionaldoesn’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-catchand swallow the exception inside the method, theTransactionInterceptorcan’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.