BeanPostProcessor

A BeanPostProcessor sees every bean as it is created, and may change or replace it before the container hands it out.

Why

Cross-cutting features are built this way: wrapping beans in proxies (AOP, caching, transactions), checking or logging every bean, injecting something special. Applications rarely write one; the starters and annotations bring the ones they need.

A processor has two methods. Both receive the bean and its name and return the object to go on with. The defaults return the bean unchanged.

Init callbacks always run on the raw instance, even when postProcessBeforeInitialization returned a wrapper.

Registration and order

An application context finds the post-processor beans by itself and creates them before the other beans. They are ordered in three groups: PriorityOrdered, then Ordered or @Order, then the rest; within a group by order value. The beans of a group are created only after the previous group is registered, so a PriorityOrdered processor already applies to the creation of the Ordered ones.

@PostConstruct methods run at a fixed place in that chain, as in Spring: after the postProcessBeforeInitialization of the PriorityOrdered processors with a lower order, before the Ordered and plain ones. afterPropertiesSet and the init method run after all of them.

On a bare factory, register processors with beanFactory.addBeanPostProcessor(...). They run in the order added.

A processor only sees beans created after it is registered. Keep its own dependencies few: a bean it depends on is created before all processors exist, and misses some of them.

Specialized processors

Class Adds
DestructionAwareBeanPostProcessor postProcessBeforeDestruction(bean, beanName), called before the bean's @PreDestroy and destroy callbacks, and requiresDestruction(bean).
InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation(beanType, beanName), which may return the bean itself and skip its creation; postProcessAfterInstantiation(bean, beanName), which may skip field and setter injection; postProcessProperties(bean, beanName), after that injection.
SmartInstantiationAwareBeanPostProcessor predictBeanType(beanType, beanName), the type of a bean the processor replaces.
MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition(definition, beanType, beanName), once per definition, and resetBeanDefinition(beanName).

These are for framework code. Applications almost never need them.

Example

import 'package:spring_core_dart/spring_core_dart.dart';

const log = SpringLog('my_app.startup');

// Logs how long the init callbacks of each bean took
// (afterPropertiesSet and the init method).
@Component()
class TimingPostProcessor extends BeanPostProcessor implements Ordered {
  final _started = <String, Stopwatch>{};

  @override
  int get order => 100;

  @override
  Object postProcessBeforeInitialization(Object bean, String beanName) {
    _started[beanName] = Stopwatch()..start();
    return bean;
  }

  @override
  Object postProcessAfterInitialization(Object bean, String beanName) {
    final watch = _started.remove(beanName);
    if (watch != null) {
      log.info('$beanName ready in ${watch.elapsedMicroseconds} us');
    }
    return bean;
  }
}

// Closes every bean that is a Sink, when the context closes.
@Component()
class ClosingPostProcessor extends DestructionAwareBeanPostProcessor {
  @override
  bool requiresDestruction(Object bean) => bean is Sink<Object?>;

  @override
  void postProcessBeforeDestruction(Object bean, String beanName) =>
      (bean as Sink<Object?>).close();
}

Members

Method Default Meaning
Object postProcessBeforeInitialization(Object bean, String beanName) returns bean Called after injection and the aware callbacks, before the init callbacks.
Object postProcessAfterInitialization(Object bean, String beanName) returns bean Called after the init callbacks. The result is what the container exposes.

The order of creation for one bean:

  1. constructor;
  2. injected fields, setters and @Autowired methods;
  3. aware callbacks;
  4. postProcessBeforeInitialization, with @PostConstruct at its place in the chain;
  5. InitializingBean.afterPropertiesSet;
  6. the initMethod of @Bean;
  7. postProcessAfterInitialization.

In Spring (Java)

@Component
class TimingPostProcessor implements BeanPostProcessor, Ordered {
  @Override
  public Object postProcessAfterInitialization(Object bean, String name) {
    log.info("ready: " + name);
    return bean;
  }

  @Override
  public int getOrder() { return 100; }
}

Differences that matter:

See also