Explain @Bean's 'inferred' destroyMethod and why an AutoCloseable third-party bean sometimes gets close() called without you configuring anything.
answer
- default destroyMethod = "(inferred)", not empty
- public close()/shutdown() auto-called
- covers AutoCloseable (HikariDataSource etc.)
- destroyMethod="" disables it
- @Bean only — NOT @Component scanning
basics
~10 s@Bean's destroyMethod defaults to the sentinel "(inferred)": if the bean has a public close() or shutdown() method (or implements AutoCloseable), Spring calls it automatically on shutdown. Set destroyMethod="" to turn this off.
solid answer
~50 sOn a @Bean factory method the destroyMethod attribute isn't empty by default — it's the special value AbstractBeanDefinition.INFER_METHOD, i.e. "(inferred)". At shutdown Spring inspects the bean and, if it finds a public no-arg close() or shutdown() method — which covers anything implementing AutoCloseable/Closeable, like DataSource pools, ExecutorService wrappers, or clients — it invokes that automatically. This is why registering a HikariDataSource or similar as a @Bean gives you correct cleanup for free. Two important controls: set destroyMethod = "" to disable inference (needed when close() has side effects you don't want, or the container calling it conflicts with another lifecycle owner); and note inference applies only to @Bean methods, not to @Component-scanned classes. @Component beans still need @PreDestroy or DisposableBean. This inference is a convenience that reduces boilerplate but can surprise you if a close() gets called you didn't expect.
code
java · 18 linesimport org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class Infra {
// close() invoked automatically on shutdown via inference
@Bean
ExecutorServiceWrapper pool() {
return new ExecutorServiceWrapper(); // has public close()/shutdown()
}
// Inference disabled: Spring will NOT call close() here
@Bean(destroyMethod = "")
SharedClient sharedClient() {
return SharedClient.global(); // lifecycle owned elsewhere
}
}go deeper
Know that @Bean can auto-call close()/shutdown() on shutdown.
Know the '(inferred)' default and how to disable it with destroyMethod="".
Know inference is @Bean-only, not for scanned components, and why you'd opt out.
Discuss double-close hazards, ownership of shared resources, and interaction with explicit/annotation destroy hooks.
**The mechanism.** The `@Bean` annotation's `destroyMethod` attribute has a default value that is *not* the empty string — it is the sentinel constant `AbstractBeanDefinition.INFER_METHOD`, whose literal value is `"(inferred)"`. When Spring builds the bean definition for a `@Bean` method and sees this sentinel, it enables **destroy-method inference**: at container shutdown it looks on the bean's class for a **public, no-argument method named `close()` or `shutdown()`** and, if found, registers it as the destroy method and invokes it. Anything implementing `java.lang.AutoCloseable` (and therefore `java.io.Closeable`) has a `close()`, so such beans are cleaned up automatically. **Why it exists / when it helps.** A huge number of resource-holding third-party types — `HikariDataSource`, `ExecutorService`-backed beans, HTTP clients, Kafka producers, caches — expose `close()`/`shutdown()`. Before inference you had to remember `@Bean(destroyMethod = "close")` on each. Inference makes the common case zero-config: declare the bean, get orderly shutdown. ```java @Bean DataSource dataSource() { return new HikariDataSource(config); // close() called automatically on shutdown } ``` **How to opt out.** Set `destroyMethod = ""` (empty string) to **disable** inference and any destroy invocation: ```java @Bean(destroyMethod = "") SomeClient client() { return SomeClient.create(); } ``` You'd do this when: the object's `close()` is managed elsewhere (another owner already closes it and a double-close throws), the bean is a proxy/shared resource that must outlive the context, or `close()`/`shutdown()` has semantics you don't want triggered by Spring. **Scope of the feature — a key gotcha.** Inference is a property of **`@Bean` factory methods and XML `<bean>` definitions only**. It does **not** apply to `@Component`/`@Service`/`@Repository` classes discovered by component scanning. A scanned `AutoCloseable` component will **not** have `close()` auto-invoked; you must add `@PreDestroy` or implement `DisposableBean`. This asymmetry trips people who assume 'Spring always calls close() on AutoCloseables.' **Interaction with explicit config.** If you name a `destroyMethod` explicitly (e.g. `"stop"`), that overrides inference — Spring calls exactly that method and does not additionally hunt for `close()`. If both a `@PreDestroy`/`DisposableBean` and an inferred/declared destroy method exist, all fire in the standard order (annotation → interface → declared/inferred method). **Prototype caveat still applies.** Inference doesn't rescue prototypes — since the container doesn't track prototypes, even an inferred `close()` is never called on them. **Interview framing.** The crisp answer: default `destroyMethod` = `"(inferred)"`; Spring auto-invokes public `close()`/`shutdown()` (covers `AutoCloseable`) on `@Bean`s; disable with `destroyMethod = ""`; and it's `@Bean`-only, not for scanned components.
- Does destroy-method inference apply to a @Component that implements AutoCloseable?No. Inference is only for @Bean methods and XML definitions. A scanned @Component needs @PreDestroy or DisposableBean; its close() is not auto-invoked.
- When would you deliberately set destroyMethod = ""?When close()/shutdown() is owned by another party and a Spring-triggered call would double-close or has unwanted side effects, or the resource must outlive the context.
saying these in an interview costs you the question
- Believing Spring auto-closes every AutoCloseable including component-scanned ones
- Thinking the default destroyMethod is an empty string (it's the '(inferred)' sentinel)
- Assuming inference also destroys prototype beans