background

BeanPostProcessor is one of the Spring extension mechanisms, and its place in the Bean lifecycle is as follows:



It can be used to do things before and after bean instantiation, such as collecting and processing custom annotations to assign values to member variables.

The problem

There are now three classes, AnnotationProcessor for custom annotations, Anno for custom annotations, and MyProcessor relies on AnnotationProcessor to process the results.

@Component
public class AnnotationProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if(field.getAnnotation(Anno.class) ! = null) {// do something about the field}}returnbean; }}Copy the code

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno {

}Copy the code

@Component
public class MyProcessor implements BeanPostProcessor {

    @Anno
    private String str;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        returnnull; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// STR is nullreturnnull; }}Copy the code

After the program to run, will found MyProcessor# postProcessAfterInitialization () to get the STR is empty, why is this so? How do I make STR already processed by AnnotationProcessor?

Problem analysis

We can look at the source code to see how Spring handles the BeanPostProcessor. Since MyProcessor is itself a bean, we can go to where Spring instantiates MyProcessor and see what is in the corresponding BeanPostProcessor collection at the time.

Code to AbstractAutowireCapableBeanFactory# doCreateBean (position), BeanPostProcessor processing is done here:







As you can see, when you instantiate MyProcessor, there are only some Spring built-in BeanPostProcessors in the BeanPostProcessor collection that is registered with the container at the time, and there are no custom BeanPostProcessors at this point

We’ll see how BeanPostProcessor registration, code to PostProcessorRegistrationDelegate# registerBeanPostProcessors () (part of the code omitted)

public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

  	// 1, register the BeanPostProcessor that implements PriorityOrdered
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

  	// 2. Register the BeanPostProcessor implemented by Ordered interface
	sortPostProcessors(orderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        List
       
         nonOrderedPostProcessors = new ArrayList
        
         ();
        
       
	for (String ppName : nonOrderedPostProcessorNames) {
            / / here will trigger AnnotationBeanPostProcessor instantiated, but at the moment registerBeanPostProcessors (the beanFactory, nonOrderedPostProcessors) does not perform, AnnotationBeanPostProcessor unregistered in the BeanFactory, so get the AnnotationBeanPostProcessor processing result in the sample
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
	    nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
		internalPostProcessors.add(pp);
            }
	}
	registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

  	// register the BeanPostProcessor for Spring internal use
	sortPostProcessors(internalPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, internalPostProcessors);
}Copy the code

Here, we can see that our custom BeanPostProcess is processed in step 3, which instantiates BeanPostProcess and registers it with the container, so the MyProcessor in our example is not processed by the AnnotationProcessor.

So how can it be dealt with? You can see that steps 1 and 2 before step 3 register the BeanPostProcessor that implements the PriorityOrdered or Ordered interface, so if we make AnnotationProcessor implement either of those interfaces, that will do the trick.

conclusion

It follows that if you want the MyProcessor to use attributes that have been processed by the AnnotationProcessor, you need to have the AnnotationProcessor implement the PriorityOrdered or Ordered interface.

The last

What optimizations does Spring have for handling BeanPostProcessor dependencies?

The BeanPostProcessor of the latter process can depend on the results of the previous BeanPostProcessor. For a BeanPostProcessor with a priority, this can be done by priority. For a non-priority BeanPostProcessor, it depends on the order of declaration.

emm… Does that make sense to play BeanPostProcessor?

Spring probably designed the BeanPostProcessor so that the BeanPostProcessor would not depend on the results of other BeanPostprocessors. Each BeanPostProcessor should process logic independently.