Annotation-based Container
- How does annotation based container works?
- What is BeanPostProcessor and how does it allow for direct modification of beans?
- How are several default BeanPostProcessors (ConfigurationClassPostProcessor, AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, EventListenerMethodProcessor) registered?
BeanPostProcessor
Spring uses BeanPostProcessor in conjunction with annotations to make the core IOC container aware of specific
annotations. BeanPostProcessor interface enables modification to bean instances before and after initialization.
Spring provides multiple built-in implementation of BeanPostProcessor to handle specific annotations and
configuration, making annotation-based dependency injection possible.
BeanPostProcessor interface defines callback methods that you can implement to provide your own (or override the
container’s default) instantiation logic, dependency resolution logic, and so forth.
public interface BeanPostProcessor {
default @Nullable Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
default @Nullable Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
}How does BeanPostProcessor work?
-
Bean Instantiation:
BeanPostProcessorinstances operates on bean instances. That is, the Spring IoC container instantiates a bean instance and thenBeanPostProcessorinstances do their work. -
Dependency Injection: If applicable, Spring injects dependencies into the bean using constructor, setter, or field injection.
-
postProcessBeforeInitialization: This method is called before the bean’s initialization callbacks (e.g.,@PostConstruct,afterPropertiesSetfromInitializingBean). It allows modifications such as custom proxy wrapping or default property setting. The returned bean instance may be a wrapper around the original. -
Initialization Phase: If the bean implements
InitializingBean, itsafterPropertiesSet()method is called. If annotated with@PostConstruct, that method runs. -
postProcessAfterInitialization: This method is called after initialization, allowing for further modifications, such as adding AOP proxies or registering the bean with other components. The returned bean instance may be a wrapper around the original. In case of aFactoryBean, this callback will be invoked for both theFactoryBeaninstance and the objects created by theFactoryBean(as of Spring 2.0). The post-processor can decide whether to apply to either theFactoryBeanor created objects or both through correspondingbean instanceof FactoryBeanchecks. This callback will also be invoked after a short-circuiting triggered by aInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiationmethod, in contrast to all otherBeanPostProcessorcallbacks. -
The fully configured bean is now available for use within the application context.
BeanPostProcessor instances are scoped per-container. This is relevant only if you use container hierarchies. If
you define a BeanPostProcessor in one container, it post-processes only the beans in that container. In other words,
beans that are defined in one container are not post-processed by a BeanPostProcessor defined in another container,
even if both containers are part of the same hierarchy.
If you write your own
BeanPostProcessor, you should consider implementing theOrderedinterface too. To change the actual bean definition (that is, the blueprint that defines the bean), you instead need to use aBeanFactoryPostProcessor.
Example of Custom BeanPostProcessor
A simple implementation of BeanPostProcessor that modifies bean properties before initialization:
@Componentpublic class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof SomeBean) { ((SomeBean) bean).setSomeProperty("Modified by BeanPostProcessor"); } return bean; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }}Built-in BeanPostProcessor Implementations in Spring
Spring provides several BeanPostProcessor implementations that process annotations and configure beans dynamically.
ConfigurationClassPostProcessor
- Purpose: Processes
@Configurationand@Beanannotations to register additional bean definitions. - Implementation:
- Scans for
@Configurationclasses. - Parses methods annotated with
@Bean. - Registers bean definitions dynamically.
- Scans for
Relevant Code (Excerpt from Spring Source)
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Scan and process @Configuration classes processConfigBeanDefinitions(beanFactory); }}AutowiredAnnotationBeanPostProcessor
- Purpose: Handles
@Autowiredand@Valuedependency injection. - Implementation:
- Scans for
@Autowiredfields and methods. - Uses reflection to inject dependencies.
- Supports constructor and setter injection.
- Scans for
Relevant Code (Excerpt from Spring Source)
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { // Resolve @Autowired dependencies injectAutowiredDependencies(bean, beanName); return pvs; }}CommonAnnotationBeanPostProcessor
- Purpose: Supports JSR-250 annotations like
@PostConstructand@PreDestroy. - Implementation:
- Detects lifecycle annotations (
@PostConstruct,@PreDestroy). - Calls the respective methods at the correct lifecycle stage.
- Detects lifecycle annotations (
Relevant Code (Excerpt from Spring Source)
public class CommonAnnotationBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { // Invoke @PostConstruct methods invokeInitMethods(bean); return bean; }
@Override public void postProcessBeforeDestruction(Object bean, String beanName) { // Invoke @PreDestroy methods invokeDestroyMethods(bean); }}PersistenceAnnotationBeanPostProcessor
- Purpose: Processes JPA-related annotations like
@PersistenceContext. - Implementation:
- Injects
EntityManagerinstances into beans marked with@PersistenceContext.
- Injects
Relevant Code (Excerpt from Spring Source)
public class PersistenceAnnotationBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { // Inject EntityManager for @PersistenceContext processPersistenceAnnotations(bean); return bean; }}EventListenerMethodProcessor
- Purpose: Enables
@EventListenerto register event listeners dynamically. - Implementation:
- Scans for methods annotated with
@EventListener. - Registers event listeners with the
ApplicationEventMulticaster.
- Scans for methods annotated with
Relevant Code (Excerpt from Spring Source)
public class EventListenerMethodProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // Register event listener methods processEventListenerMethods(bean); return bean; }}How These PostProcessors Are Registered
Spring automatically registers these post-processors when annotation-based configuration is enabled.
Implicit Registration via AnnotationConfigApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);- This automatically registers necessary post-processors.
Explicit Registration in XML Configuration
<context:annotation-config/>With Spring Boot
@SpringBootApplication is the key entry point. When Spring Boot starts up, it automatically configures the
application context using the @EnableAutoConfiguration annotation.
Spring Boot scans the classpath and automatically registers beans, including post-processors, based on the presence
of specific libraries or annotations. For example, if Spring Boot detects that spring-boot-starter-data-jpa is in
the classpath, it will register the PersistenceAnnotationBeanPostProcessor for handling persistence-related annotations.
ConfigurationClassPostProcessor
- This is registered by Spring Boot through the
@EnableAutoConfigurationmechanism. - The
ConfigurationClassPostProcessoris part of the internal Spring context, and it’s automatically registered by Spring Boot when it starts up. It is registered as a@Beanin Spring’s internal context configuration. - Spring Boot’s internal configuration class, SpringApplication, indirectly enables this by invoking the
AnnotationConfigApplicationContext(which is triggered by Spring Boot’s@SpringBootApplication), which triggers the post-processor registration.
AutowiredAnnotationBeanPostProcessor
- This processor is automatically registered by Spring during the application context initialization phase,
specifically when the
AnnotationConfigApplicationContextis initialized . - Spring Boot behavior: When Spring Boot starts,
@EnableAutoConfigurationensures that theAutowiredAnnotationBeanPostProcessoris registered, which allows for automatic dependency injection using@Autowired.
CommonAnnotationBeanPostProcessor
- This is also registered automatically as part of the Spring container initialization. It is included in Spring Boot’s
auto-configuration and is added to the
ApplicationContextas part of the default configuration. - When the context is created,
CommonAnnotationBeanPostProcessoris registered to handle annotations that require lifecycle processing (like@PostConstruct). The post-processor is defined in theAnnotationConfigApplicationContext.
PersistenceAnnotationBeanPostProcessor
- This is automatically registered in Spring Boot when Spring Data JPA or other persistence mechanisms are available in the classpath.
- Spring Boot’s auto-configuration detects the presence of JPA dependencies (such as Hibernate or Spring Data JPA) and registers this post-processor if it finds them.
- This happens through
@EnableAutoConfiguration, which ensures that the correct beans related to persistence are created and that the post-processor is registered to handle injection of persistence-related annotations.
EventListenerMethodProcessor
- This post-processor is automatically registered when Spring Boot starts, and is part of the event handling system.
- Spring Boot enables event handling by registering this processor in the context. This occurs as part of Spring’s
default application context initialization when
@EnableAutoConfigurationis invoked. - If Spring Boot detects event-related beans or configurations (such as an
@EventListener-annotated method), it automatically wires up this post-processor to handle the event listeners.