Spring Bean Lifecycle and Prototype Scope

Last update: 2025-11-17 23:19:46 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Spring separates bean metadata from bean instances. A BeanDefinition describes how to create an object, while a BeanFactory applies that definition when the object is requested. An ApplicationContext adds coordinated startup, post-processing, events, resource loading, and shutdown.

What Happens During Context Refresh #

AbstractApplicationContext.refresh() prepares the environment and bean factory before application singletons become usable. The major stages are:

  1. Prepare or obtain the BeanFactory and load bean definitions.
  2. Invoke BeanFactoryPostProcessors, including configuration-class processing, while definitions can still be changed.
  3. Discover and register BeanPostProcessors.
  4. Initialize context infrastructure such as message resolution, events, and context-specific resources.
  5. Instantiate the remaining non-lazy singleton beans.
  6. Publish the refreshed state and ContextRefreshedEvent.

A BeanFactoryPostProcessor changes metadata. A BeanPostProcessor instead sees instances and can inject fields, invoke lifecycle annotations, or replace a bean with a proxy. Creating ordinary application beans from a factory post-processor is unsafe because it can instantiate them before all post-processors are registered.

Dependency Order and depends-on #

The container creates a bean’s constructor and property dependencies before the dependent bean. Constructor injection therefore expresses both a required relationship and its creation order:

java
@Component
final class ReportService {
    private final ReportRepository repository;

    ReportService(ReportRepository repository) {
        this.repository = repository;
    }
}

@DependsOn("schemaInitializer") or XML depends-on adds an order when no injection relationship exists, such as initialization of a shared external resource. For singleton beans it also reverses destruction order: dependent beans are destroyed before the beans on which they depend. depends-on should not replace an actual typed dependency because it hides what the class uses.

Instance Initialization #

For a normal bean, Spring performs these operations:

  1. Instantiate the object, normally by invoking a constructor or factory method.
  2. Resolve and inject properties and fields.
  3. Invoke aware callbacks, including BeanNameAware, BeanFactoryAware, and ApplicationContextAware.
  4. Invoke each BeanPostProcessor before initialization.
  5. Invoke @PostConstruct, then InitializingBean.afterPropertiesSet(), then a configured custom init method.
  6. Invoke each BeanPostProcessor after initialization.

Spring infrastructure implements portions of the sequence through post-processors, so custom processors must honor ordering contracts. An after-initialization processor may return a proxy rather than the original object. Callers receive the ready, possibly advised object only after the full chain finishes.

Constructor injection is the safest place to establish required invariants. @PostConstruct is appropriate when initialization depends on injected properties or container services that are unavailable in the constructor.

Destruction and Context Shutdown #

Closing a configurable context destroys managed singletons. For each bean, Spring invokes @PreDestroy, DisposableBean.destroy(), and a configured custom destroy method, subject to the adapter’s duplicate-method checks. SmartLifecycle adds phased stop callbacks for components that need coordinated shutdown.

Applications should close manually created contexts, or register a shutdown hook with registerShutdownHook(). Spring Boot closes its application context as part of normal process shutdown. Abrupt process termination cannot guarantee callbacks, so durable correctness must not depend solely on them.

Prototype Ownership #

Prototype scope means that each container request produces a new instance. When a prototype is injected directly into a singleton, resolution happens only while the singleton is created; the same prototype instance remains in that field. Use ObjectProvider<T>, Jakarta Provider<T>, method injection, or a scoped proxy when each operation needs a new instance.

Spring initializes a prototype completely and returns it to the caller, but it does not retain the instance or invoke destruction callbacks later. Ownership therefore transfers to the recipient. Code that obtains a prototype holding a file, socket, or executor must close it explicitly. A custom BeanPostProcessor can track such instances when centralized cleanup is required, but explicit resource ownership remains clearer.

Primary References #

Comments

Read or comment on GitHub (sign-in required to comment)