Spring Container Types

Last update: 2023-12-14 04:20:44 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Spring’s container API has three distinct layers: bean metadata, the factory that creates objects from that metadata, and an application context that coordinates the factory with application services. Keeping those layers separate makes Spring source code and extension points easier to reason about.

Bean Definitions and Registries #

BeanDefinition is mutable metadata for one bean. It records the bean class or class name, scope, constructor arguments, property values, qualifiers, factory method, lifecycle methods, role, source location, and explicit dependencies. The parent/child metadata relationship is independent of Java inheritance.

Common implementations serve different registration paths:

  • RootBeanDefinition represents a merged definition used by a bean factory.
  • ChildBeanDefinition inherits metadata from a named parent definition.
  • GenericBeanDefinition can express either standalone or parent-derived metadata before merging.
  • AnnotatedGenericBeanDefinition exposes annotation metadata for a registered class.
  • ScannedGenericBeanDefinition exposes metadata read during classpath scanning without first loading the component class.

BeanDefinitionRegistry stores definitions by bean name. Its central operations register, remove, and query definitions; DefaultListableBeanFactory and GenericApplicationContext are important implementations.

AnnotatedBeanDefinitionReader registers explicitly supplied component classes:

java
var context = new GenericApplicationContext();
var reader = new AnnotatedBeanDefinitionReader(context);
reader.register(OrderService.class);
context.refresh();

ClassPathBeanDefinitionScanner searches packages for candidate components and registers their definitions. Both mechanisms apply annotation scope, naming, qualifiers, conditions, and BeanDefinitionCustomizer callbacks. A customizer can make a targeted metadata change during registration without implementing a registry post-processor.

BeanFactory, FactoryBean, and ObjectFactory #

BeanFactory is the runtime registry and dependency-resolution API. It locates definitions, creates beans, resolves collaborators, manages singleton identity, and applies lifecycle processors. ListableBeanFactory adds enumeration, HierarchicalBeanFactory adds parent lookup, and ConfigurableListableBeanFactory exposes container configuration to framework infrastructure. Application code should depend on its domain collaborators rather than call getBean() as a service locator.

Three similarly named types have different responsibilities:

  • A BeanFactory is the container.
  • A FactoryBean<T> is a bean that creates another object. Looking up its name returns the product; prefixing the name with & returns the factory itself. getObjectType() and isSingleton() describe the product.
  • An ObjectFactory<T> is a minimal callback with getObject(). Spring uses it for deferred retrieval and internal creation callbacks. ObjectProvider<T> extends the model with optional, ordered, and stream-based access.

The Application Context Hierarchy #

ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver, and related environment capabilities. ConfigurableApplicationContext adds lifecycle operations such as refresh(), close(), environment configuration, and access to the configurable factory.

AbstractApplicationContext implements the refresh algorithm. Specialized base classes decide how definitions reach the factory:

  • GenericApplicationContext owns one internal DefaultListableBeanFactory. Definitions can be registered through its registry API before one refresh() call.
  • AnnotationConfigApplicationContext combines a generic context with an AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner. Its constructors can register classes or scan packages and then refresh.
  • Servlet-aware contexts add web scopes and servlet resources but retain the same core factory contracts.

Some older context implementations recreate a bean factory during refresh, while GenericApplicationContext does not support repeated refresh attempts. Framework extensions must follow the concrete context’s lifecycle rather than assume every context is refreshable more than once.

Refresh and Generated Definitions #

Annotation registration initially creates definitions for the supplied classes. During refresh, ConfigurationClassPostProcessor parses @Configuration, component scans, imports, and @Bean methods, then registers additional definitions before ordinary beans are instantiated. Registry post-processors can generate more definitions in the same phase. Spring’s AOT processing can generate registration code ahead of time, but the resulting definitions still feed the same bean-factory model.

ApplicationStartup instruments startup without changing lifecycle semantics. DefaultApplicationStartup is a low-overhead no-op implementation, BufferingApplicationStartup retains bounded step data, and FlightRecorderApplicationStartup emits Java Flight Recorder events. A StartupStep has a name, tags, and explicit end; custom infrastructure should keep tags cheap because startup instrumentation runs on the critical path.

Choosing an Extension Point #

Use the narrowest layer that matches the requirement:

  1. Customize a definition at registration with BeanDefinitionCustomizer.
  2. Add or transform metadata with BeanDefinitionRegistryPostProcessor or BeanFactoryPostProcessor.
  3. intercept instances with BeanPostProcessor.
  4. Build an object-producing bean with FactoryBean.
  5. Build a full coordinated container on GenericApplicationContext or an application-context subtype.

Primary References #

Comments

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