skip to content

Explain @Bean's 'inferred' destroyMethod and why an AutoCloseable third-party bean sometimes gets close() called without you configuring anything.

level: seniorimportance: should knowfreq 40%

answer

  1. default destroyMethod = "(inferred)", not empty
  2. public close()/shutdown() auto-called
  3. covers AutoCloseable (HikariDataSource etc.)
  4. destroyMethod="" disables it
  5. @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 s

On 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 lines
java
import 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

for a junior

Know that @Bean can auto-call close()/shutdown() on shutdown.

for a middle

Know the '(inferred)' default and how to disable it with destroyMethod="".

for a senior

Know inference is @Bean-only, not for scanned components, and why you'd opt out.

for a principal

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

context