Spring – BeanFactoryAware extension interface

Article directory
  • Pre
  • org.springframework.beans.factory.BeanFactoryAware
  • Extension point description
  • Aware interface
  • Execution timing and order of Spring’s built-in Aware interface
  • Source code analysis (direct call)
  • Source code analysis (BeanPostProcessor call execution sequence)
  • Extension point example

Pre

Spring Boot – List of extension interfaces

org.springframework.beans.factory.BeanFactoryAware

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;

/**
 * Interface to be implemented by beans that wish to be aware of their
 * owning {@link BeanFactory}.
 *
 * <p>For example, beans can look up collaborating beans via the factory
 * (Dependency Lookup). Note that most beans will choose to receive references
 * to collaborating beans via corresponding bean properties or constructor
 * arguments (Dependency Injection).
 *
 * <p>For a list of all bean lifecycle methods, see the
 * {@link BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Chris Beams
 * @since 11.03.2003
 * @see BeanNameAware
 * @see BeanClassLoaderAware
 * @see InitializingBean
 * @see org.springframework.context.ApplicationContextAware
 */
public interface BeanFactoryAware extends Aware {

/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after the population of normal bean properties
* but before an initialization callback such as
* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
* @param beanFactory owning BeanFactory (never {@code null}).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

Extension point description

The extension point method is setBeanFactory, and you can get the BeanFactory attribute.

Usage scenario: You can get BeanFactory after the bean is instantiated but before it is initialized. At this time, each bean can be specially customized. Or you can get BeanFactory for caching and use it later.

Aware interface

  • Built-in Aware interface for Spring core and context

    ApplicationEventPublisherAware
    MessageSourceAware
    ResourceLoaderAware
    BeanFactoryAware
    EnvironmentAware
    EmbeddedValueResolverAware
    ImportAware
    LoadTimeWeaverAware
    BeanClassLoaderAware
    BeanNameAware
    ApplicationContextAware

  • Spring web’s built-in Aware interface

    ServletContextAware
    ServletConfigAware

  • Spring’s other built-in Aware interfaces

    SchedulerContextAware (spring scheduling)
    NotificationPublisherAware (spring jmx export)
    BootstrapContextAware (spring jca)

Execution timing and sequence of Spring’s built-in Aware interface

The execution time of the Aware interface must be when the Spring Bean is created.

There are mainly two modes for the implementation of the Aware interface:

  • When initializing the Bean (initializeBean), directly call the method -> setXXXX
  • BeanPostProcessor -> Object postProcessBeforeInitialization(Object bean, String beanName)

processor.postProcessBeforeInitialization(result, beanName);

Enter

Spring’s built-in core Aware BeanPostProcessor is ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

Let’s continue to look at invokeAwareInterfaces(bean);

It can be seen that ApplicationContextAwareProcessor is associated with most of Spring’s built-in Aware interfaces, and their execution order is as follows:

EnvironmentAware -> EmbeddedValueResolverAware -> ResourceLoaderAware -> ApplicationEventPublisherAware -> MessageSourceAware -> ApplicationStartupAware -> ApplicationContextAware

Conclusion: The timing of direct method calls is earlier than the timing of calls through BeanPostProcessor#postProcessBeforeInitialization

Since the calling of the Aware interface is directly affected by BeanPostProcessor, the execution sequence of BeanPostProcessor is also the calling sequence of the Aware interface.

Then why did you jump directly to ApplicationContextAwareProcessor?

Let’s take a look at the setting execution timing of ApplicationContextAwareProcessor.

AbstractApplicationContext#refresh method

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

      //....omit the rest of the code
}

Continue to see prepareBeanFactory

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //....omit the rest of the code
        // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

      //....omit the rest of the code
}

You can see beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)Here you can see that ApplicationContextAwareProcessor is directly added to the BeanFactory first.

Source code analysis (direct call)

org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors()
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
org.springframework.beans.factory.support.AbstractBeanFactory#getBean
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

In the doCreateBean method

Focus on

 // Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

Enter org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean

if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}

look

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods


private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

See it?

if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}

From the above code snippets, it can be concluded that the execution order of the Aware interface is

 BeanNameAware -> BeanClassLoaderAware -> BeanFactoryAware

The call chain is as follows

org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors()
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
org.springframework.beans.factory.support.AbstractBeanFactory#getBean
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods

Source code analysis (BeanPostProcessor call execution sequence)

In fact, it is already in the first screenshot above, let’s take a look at it again

org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors()
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
org.springframework.beans.factory.support.AbstractBeanFactory#getBean
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean

Continue to see initializeBean

//....omitted
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
 //....omitted



@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
  throws BeansException {

  Object result = existingBean;
  for (BeanPostProcessor processor : getBeanPostProcessors()) {
    Object current = processor.postProcessBeforeInitialization(result, beanName);
    if (current == null) {
      return result;
    }
    result = current;
  }
  return result;
}

public List<BeanPostProcessor> getBeanPostProcessors() {
  return this.beanPostProcessors;
}

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

It can be seen that beanPostProcessors is an ordered list associated with the BeanFactory. The data source of this list is the ConfigurableBeanFactory#addBeanPostProcessor(BeanPostProcessor beanPostProcessor) method.

Return to AbstractApplicationContext#refresh()

registerBeanPostProcessors(beanFactory);//Inject BeanPost into BeanFactory

The final executor of registering BeanPostProcessor is PostProcessorRegistrationDelegate.registerBeanPostProcessors

The ordering rules are as follows (for the BeanPostProcessor BeanDefinition belonging to the BeanFactory):

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
  • Implemented the PriorityOrdered interface with the highest priority, and then sorted by order Small -> Large
  • The second is to implement the Ordered interface, and then sort by order small -> large
  • Others are based on the BeanDefinition Spring registration order.

Of course, you can also configure the BeanFactory through BeanFactoryPostProcessor, for example ConfigurationClassPostProcessor

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  int factoryId = System.identityHashCode(beanFactory);
  if (this.factoriesPostProcessed.contains(factoryId)) {
    throw new IllegalStateException(
      "postProcessBeanFactory already called on this post-processor against " + beanFactory);
  }
  this.factoriesPostProcessed.add(factoryId);
  if (!this.registriesPostProcessed.contains(factoryId)) {
    // BeanDefinitionRegistryPostProcessor hook apparently not supported...
    // Simply call processConfigurationClasses lazily at this point then.
    processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
  }

  enhanceConfigurationClasses(beanFactory);
  //Register to add ImportAware interface processor
  beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}





private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    ....

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) {
    if (bean instanceof ImportAware) {
      ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
      AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
      if (importingClass != null) {
        //ImportAware#setImportMetadata call
        ((ImportAware) bean).setImportMetadata(importingClass);
      }
    }
    return bean;
  }
}

It can be seen from the above that the execution order of ImportAware is executed after the Aware interfaces associated with ApplicationContextAwareProcessor.

In fact, there is also a LoadTimeWeaverAwareProcessor

AbstractApplicationContext#prepareBeanFactory


// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {//Conditional injection, the bean or bean definition must exist
  beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  // Set a temporary ClassLoader for type matching.
  beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

The current Aware interface execution sequence is as follows:

BeanNameAware -> BeanClassLoaderAware -> BeanFactoryAware -> EnvironmentAware -> EmbeddedValueResolverAware -> ResourceLoaderAware -> ApplicationEventPublisherAware -> MessageSourceAware -> ApplicationContextAware -> ImportAware -> LoadTimeWeaverAware

Extension point example

package com.artisan.bootspringextend.testextends;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.annotation.Configuration;

/**
 * @author little craftsman
 * @version 1.0
 * @description: TODO
 * @date 2022/12/4 9:53
 * @mark: show me the code, change the world
 */

@Slf4j
@Configuration
public class ExtendBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("----->ExtendBeanFactoryAware {}" , beanFactory.getBean(ExtendBeanFactoryAware.class).getClass().getSimpleName());
    }
}

Let’s look at the second, more complete extension

 package com.artisan.bootspringextend.testextends;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.*;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

/**
 * @author little craftsman
 * @version 1.0
 * @description: TODO
 * @date 2022/12/4 11:02
 * @mark: show me the code, change the world
 */
@Slf4j
@Component
public class ExtendInvokeAware implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware,
        ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware,ApplicationStartupAware, ApplicationContextAware, ImportAware,
        LoadTimeWeaverAware {

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        log.info(" ------> BeanClassLoaderAware::setBeanClassLoader invoked");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info(" ------> BeanFactoryAware::setBeanFactory invoked");
    }

    @Override
    public void setBeanName(String s) {
        log.info(" ------> BeanNameAware::setBeanName invoked");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info(" ------> ApplicationContextAware::setApplicationContext invoked");
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        log.info(" ------> ApplicationEventPublisherAware::setApplicationEventPublisher invoked");
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        log.info(" ------> EmbeddedValueResolverAware::setEmbeddedValueResolver invoked");
    }

    @Override
    public void setEnvironment(Environment environment) {
        log.info(" ------> EnvironmentAware::setEnvironment invoked");
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        log.info(" ------> MessageSourceAware::setMessageSource invoked");
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        log.info(" ------> ResourceLoaderAware::setResourceLoader invoked");
    }

    @Override
    public void setApplicationStartup(ApplicationStartup applicationStartup) {
        log.info(" ------> ApplicationStartup::setApplicationStartup invoked");
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        log.info(" ------> ImportAware::setImportMetadata invoked");
    }

    @Override
    public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
        log.info(" ------> LoadTimeWeaverAware::setLoadTimeWeaver invoked");
    }


}

Output results

 . ____ _ __ _ _
 /\ / ___'_ __ _ _(_)_ __ __ _

( ( )___ | ‘_ | ‘| | ’ / ` |
/ )| |)| | | | | || (| | ) ) ) )
‘ |
| .__|| ||| |, | / / //
=========|
|==============|
/=///_/
::Spring Boot:: (v2.4.2)

2022-12-04 19:14:25.103 INFO 12832 --- [main] .b.t.ExtendApplicationContextInitializer : ExtendApplicationContextInitializer # initialize Called
2022-12-04 19:14:25.110 INFO 12832 --- [main] c.a.b.BootSpringExtendApplication : Starting BootSpringExtendApplication using Java 1.8.0_261 on LAPTOP-JF3RBRRJ with PID 12832 (D:IdeaProjectsoot2oot-spring-extend argetclasses started by artisan in D: IdeaProjectsoot2)
2022-12-04 19:14:25.111 INFO 12832 --- [main] c.a.b.BootSpringExtendApplication: No active profile set, falling back to default profiles: default
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> BeanNameAware::setBeanName invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> BeanClassLoaderAware::setBeanClassLoader invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> BeanFactoryAware::setBeanFactory invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> EnvironmentAware::setEnvironment invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> EmbeddedValueResolverAware::setEmbeddedValueResolver invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> ResourceLoaderAware::setResourceLoader invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> ApplicationEventPublisherAware::setApplicationEventPublisher invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> MessageSourceAware::setMessageSource invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> ApplicationStartup::setApplicationStartup invoked
2022-12-04 19:14:25.444 INFO 12832 --- [main] c.a.b.testextends.ExtendInvokeAware: ------> ApplicationContextAware::setApplicationContext invoked
2022-12-04 19:14:25.529 INFO 12832 --- [main] c.a.b.BootSpringExtendApplication : Started BootSpringExtendApplication in 0.747 seconds (JVM running for 1.792)

Process finished with exit code 0

syntaxbug.com © 2021 All Rights Reserved.