Location>code7788 >text

10,000-word article takes a peek at all the extension points in Spring

Popularity:582 ℃/2024-09-08 16:53:02

write sth. upfront

Spring's core idea is the container, when the container refresh, the outside looks calm, in fact, the inside is a shocking waves, the ocean is a. Springboot is more encapsulated Spring, follow the conventions greater than the configuration, coupled with theMechanisms for automated assembly. Many times we can assemble a feature with almost zero configuration by simply referencing a dependency.

provided by the spring, in the container or bean life cycle stages, for the spring framework callbacks to use the function methods, that is, the extension point. Extension points reflect the flexibility of the Spring Framework , business affinity. So that developers can not modify the spring source code in the case of additional management of the container and bean behavior, attributes.

fancyautomated assemblyTo play around with it, you have to understand spring's role for theConstructive life cycle of beanAs well as the various extensions to the interface, of course, to understand the life cycle of the bean can also promote us to deepen the understanding of spring. Business code can also make reasonable use of these extensions to write more elegant code.

Searching for spring extension points on the Internet, found that very few blog posts say the whole thing, only some commonly used extension points of the description. So in this article, I summarized almost Spring & Springboot all the extension interface, the use of each extension point scenarios, and collated a bean in spring from being loaded to the initialization to the destruction of all the extensible points of the order of the call map.

This article does not talk about the principle, but only the expansion point and the use of clear, especially the call order, the principle can be moved to theIOC Article SeriesBean life cycleWe will keep updating the corresponding principle and source code analysis. You canThink of this article as a toolkit, when forgetting the order of execution, or when forgetting how to use this extension, you can go back and look at it again.

spring扩展点执行顺序

ApplicationContextInitializer

present (sb for a job etc)

This is the entire spring container initialized before refreshing theConfigurableApplicationContextcallback interface, which is simply the interface to the callbacks in theCalled before refreshing the container this kind ofinitializemethod. The main purpose of this interface is to make some configurations or adjustments in the early stages of Spring application context initialization so that these configurations can be used after the context is loaded.

This interface.Spring Frameworkdoes not provide any implementation classes of its own, but in theSpringBootThere are more extended implementations of it.

Usage Scenarios

  1. Environment configuration at application startup: you can use theApplicationContextInitializer to perform some environment-related configurations when the application context is initialized, such as setting system properties, loading external configuration files, and so on.
public class EnvironmentInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = ();
        ("development");
        ("The configuration file is set todevelopment");
    }
}
  1. Register for customizedBeanFactoryPostProcessor orBeanPostProcessorApplicationContextInitializer It can be used to register customizedBeanFactoryPostProcessor orBeanPostProcessorto allow for some customization before or after the bean is initialized.
public class CustomBeanFactoryPostProcessorInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        (beanFactory -> {
            // Add customized BeanFactoryPostProcessor
            ("Added customizationBeanFactorypost-processor...");
        });
    }
}
  1. Dynamically addPropertySource: you can add dynamically during initializationPropertySourceto allow these properties to be used in subsequent bean definitions and initializations.
public class PropertySourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        MutablePropertySources propertySources = ().getPropertySources();
        (new MapPropertySource("customPropertySource", ("customKey", "customValue")));
        ("Added custom attribute source");
    }
}

Adding Extension Points in a Spring Environment

Customizing the Spring environment to implement aApplicationContextInitializerThere are three ways to make and it works:

  1. The manually called setXXX method adds the
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();

// Add initializer
(new TestApplicationContextInitializer());

// Set config locations and refresh context
("classpath:");
();

// Use the context
// ...

();
  1. Registration in Spring's XML configuration file
<context:initializer class=""/>
  1. File Configuration
<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value></param-value>
</context-param>

Adding Extension Points in a SpringBoot Environment

Example showing how to implement an ApplicationContextInitializer to add a custom property source:

public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        MutablePropertySources propertySources = ().getPropertySources();
        
        // Creating customized property sources
        Map<String, Object> customProperties = new HashMap<>();
        ("", "custom value");
        MapPropertySource customPropertySource = new MapPropertySource("customPropertySource", customProperties);
        
        // Adding a custom property source to the application context's property source list
        (customPropertySource);
    }
}

There are also three ways to make it work in SpringBoot:

  1. In the startup class with the(new TestApplicationContextInitializer())statement is added to the
@SpringBootApplication
public class MySpringExApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication();
        (new TestApplicationContextInitializer()); // directly in theSpringApplicationAdd
        (args);
    }
}
  1. application configuration file Configuration
# Documentation
context.
  initializer.
    classes.
  1. Spring SPIExtension, added in (officially recommended):

SpringBoot built-in ApplicationContextInitializer

  • DelegatingApplicationContextInitializer: Using Environmental PropertiesThe specified initializers do the initialization work, but if they are not specified, they do nothing. This makes it possible to configure a custom implementation class in theThe Ri became possible.

  • ContextIdApplicationContextInitializer: Sets the ID of the Spring application context, what value the Id is set to will refer to the environment property:

    • .instance_index
    • If none of these properties are present, the ID uses application.
  • ConfigurationWarningsApplicationContextInitializer: Warnings in logs for general configuration errors

  • ServerPortInfoApplicationContextInitializer: Write the actual listening port used by the built-in servlet container to the Environment environment property. This way the propertyIt is then possible to directly pass the@Valueinjected into the test, or obtained through the environment property Environment.

  • SharedMetadataReaderFactoryContextInitializer: Create a CachingMetadataReaderFactory object shared by SpringBoot and ConfigurationClassPostProcessor. Implementation class is: ConcurrentReferenceCachingMetadataReaderFactory

  • ConditionEvaluationReportLoggingListener: Write ConditionEvaluationReport to the log.

BeanFactoryPostProcessor

present (sb for a job etc)

This interface isbeanFactoryextension interface, which is invoked when spring is reading thebeanDefinitioninformation after the bean is instantiated and before the bean is instantiated. Although you can't register the beanDefinition at this point, you can take advantage of the fact that the bean isn't instantiated and you can modify the Spring container when it starts up by modifying its internalBeanDefinition. By realizingBeanFactoryPostProcessor interface, developers can modify the bean's definitional metadata, such as Scope, dependency lookup methods, initialization methods, modifying property values, adding additional metadata, and so on, before the bean is instantiated, which in turn affects the initialization behavior.

At application startup, the Spring container automatically detects and calls the postProcessBeanFactory method of all classes that implement the BeanFactoryPostProcessor interface. Developers can use this method to implement custom logic to achieve some advanced custom logic and functionality extensions. This method is called only once.Also remember not to do the instantiation of the bean here

Usage Scenarios

  1. Modifying Bean Properties: You can dynamically change certain configuration properties or inject additional dependencies.
public class PropertyModifierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = ("myBean");
        MutablePropertyValues propertyValues = ();
        ("propertyName", "newValue");
    }
}
  1. Dynamic Bean Registration: You can decide whether to register a bean based on a configuration file or system environment variable.
public class ConditionalBeanRegistrar implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (someCondition()) {
            BeanDefinitionBuilder beanDefinitionBuilder = ();
            ("myConditionalBean", ());
        }
    }

    private boolean someCondition() {
        // Customizing Conditional Logic
        return true;
    }
}
  1. Modify Bean Definition: You can modify the definition information of bean such as scope, initialization and destruction methods.
public class ScopeModifierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = ("myBean");
        (BeanDefinition.SCOPE_PROTOTYPE);
    }
}
  1. Attribute placeholder replacement: you can use thePropertyPlaceholderConfigurer realizationBeanFactoryPostProcessor interface to replace the attribute placeholders in the bean definition.
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {
        (beanFactory, props);
        // Custom Attribute Handling Logic
    }
}

Other usage scenarios:

  1. Configuration Center Integration: When you need to dynamically load the configuration and modify the bean definition from an external configuration center (such as Spring Cloud Config or Apache Zookeeper), you can use theBeanFactoryPostProcessor
  2. Multi-environment support: Dynamically modify the definition of the bean according to different environments (e.g., development, testing, production) to ensure that the configuration of the bean is different in different environments.
  3. Dynamic Registration Bean: Dynamically registers or de-registers beans at runtime based on conditions, such as the need to register certain beans only under certain conditions.
  4. Complex business applications: Sometimes you need to dynamically adjust the configuration of a bean according to complex business rules.BeanFactoryPostProcessor Very useful.

BeanDefinitionRegistryPostProcessor

present (sb for a job etc)

BeanDefinitionRegistryPostProcessor is a container-level post processor.The container-level post-processor is executed once after the Spring container is initialized and before it is refreshed, and is used to dynamically register beans to the container

Through the BeanFactoryPostProcessor subclass BeanDefinitionRegistryPostProcessor, you can register a BeanDefinition object of your own to the container, waiting for the container to be called in order to instantiate the object can be used as a bean.

BeanDefinitionRegistryPostProcessor is used to add, delete, modify and check the BeanDefintion through BeanDefinitionRegistry before instantiation after bean parsing.

The BeanFactoryPostProcessor described earlier is the parent class of this interfaceIf you implement BeanDefinitionRegistryPostProcessor, you can also override its parent class. But the implementation of the BeanDefinitionRegistryPostProcessor postProcessBeanFactory method will be executed first, and then the implementation of the BeanFactoryPostProcessor postProcessBeanFactory. Specifically look at the order of calls Figure

Usage Scenarios

  1. Modify an existing BeanDefinition: You can modify an existing BeanDefinition before it is instantiated.BeanDefinition, such as changing its property values or scopes.
public class BeanDefinitionModifier implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ("exist postProcessBeanDefinitionRegistry Modify the existing BeanDefinition");

        if (("myExistingBean")) {
            BeanDefinition beanDefinition = ("myExistingBean");
            MutablePropertyValues propertyValues = ();
            ("propertyName", "newValue");
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // This method can be left blank or used for further processing
    }
}
  1. Conditionally registering beans: Dynamically registering or unregistering certain beans based on certain conditions (e.g., environment variables, configuration files, etc.).
public class ConditionalBeanRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ("exist postProcessBeanDefinitionRegistry Registered in accordance with the conditions Bean");

        if (someCondition()) {
            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                    .genericBeanDefinition()
                    .getBeanDefinition();
            ("conditionalBean", beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // This method can be left blank or used for further processing
    }

    private boolean someCondition() {
        // Customizing Conditional Logic
        return true;
    }
}
  1. Scanning and registering custom annotated beans: implements the scanning logic for custom annotations and dynamically registers the beans marked by these annotations.
public class CustomAnnotationBeanRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ("exist postProcessBeanDefinitionRegistry Scan and register custom annotations in the Bean");

        // Customizing the scanning logic,Suppose a class is found MyAnnotatedBean
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                .genericBeanDefinition()
                .getBeanDefinition();
        ("myAnnotatedBean", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // This method can be left blank or used for further processing
    }
}
  1. dependency, for example, and if that dependency jar exists, use theredis as a cacheOtherwise, uselocal cacheThis requirement can be fully utilized in the postProcessBeanDefinitionRegistry. This requirement can be fully utilized in the postProcessBeanDefinitionRegistry to determine the dependency and register the corresponding class to the container if it exists.
@Configuration
public class AppConfig {

    @Bean
    public static BeanDefinitionRegistryPostProcessor customBeanDefinitionRegistryPostProcessor() {
        return new BeanDefinitionRegistryPostProcessor() {

            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
                ("exist postProcessBeanDefinitionRegistry Register the cache implementation class based on the conditions in the");

                try {
                    // probe Redis dependencies是否存exist
                    ("");
                    ("detected Redis dependencies,enrollment RedisCacheService");

                    AbstractBeanDefinition redisCacheBeanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition()
                            .getBeanDefinition();
                    ("cacheService", redisCacheBeanDefinition);

                } catch (ClassNotFoundException e) {
                    ("未detected Redis dependencies,enrollment LocalCacheService");

                    AbstractBeanDefinition localCacheBeanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition()
                            .getBeanDefinition();
                    ("cacheService", localCacheBeanDefinition);
                }
            }

            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                // This method can be left blank or used for further processing
            }
        };
    }
}
  1. Mybatis is using BeanFactoryPostProcessor to register the mapper.

MapperScannerConfigurer The main function is to find all packages marked with the@Mapper annotations (or other specified annotations) and register these interfaces as Spring's BeanDefinition. so that the Spring container automatically creates proxies for these Mapper interfaces at startup and injects them wherever they are needed.

followingMapperScannerConfigurer The core code snippet of the

// #postProcessBeanDefinitionRegistry
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private String basePackage;
    private ApplicationContext applicationContext;

    public void setBasePackage(String basePackage) {
         = basePackage;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         = applicationContext;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        //Set its resource loader to the current ApplicationContext
        ();
        ();
        //call (programming) () methodologies,Scan the specified package path,Find all eligible Mapper connector,and register them as Spring (used form a nominal expression) BeanDefinition。
        ();
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 此methodologies可以留空或用于进一步处理
    }
}

BeanPostProcessor

present (sb for a job etc)

BeanPostProcessor The interface defines two basic bean initialization callback methods that are executed before and after property assignment.

  • postProcessBeforeInitialization: In the bean initialization method (e.g.@PostConstruct or a custom initialization method) is called; the object returned will be the actual bean injected into the container; if null is returned, the bean will not be registered. Can be used to create proxy classes
  • postProcessAfterInitialization: After initializing the bean, the object returned will be the actual bean injected into the container; if null is returned, the bean will not be registered.

Usage Scenarios

  1. Custom logic before and after initialization: Perform some custom operations before or after the bean is initialized, such as setting some properties, performing dependency injection, performing some checks, etc.
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            ("beanpre-initialization: " + beanName);
            ((MyBean) bean).setName("Modified Name Before Initialization");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            ("beanpost-initialization: " + beanName);
        }
        return bean;
    }
}

public class MyBean {
    private String name;

    public MyBean(String name) {
         = name;
    }

    public void setName(String name) {
         = name;
    }

    public void init() {
        ("beanin the process of (doing something or happening)init: " + name);
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }

  1. Generation of proxy objects: in thepostProcessAfterInitialization method to generate a proxy object for the bean for AOP (cutter-oriented programming) or other purposes.
@Component
public class ProxyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            Enhancer enhancer = new Enhancer();
            (());
            (new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    ("Before method: " + ());
                    Object result = (obj, args);
                    ("After method: " + ());
                    return result;
                }
            });
            return ();
        }
        return bean;
    }
}
  1. Logging and monitoring: Record the initialization process of the bean, performance monitoring, logging, etc.
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        ("Start initializationbean: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ("initializationbeanclose: " + beanName);
        return bean;
    }
}

  1. Auto-assembly and injection: Auto-assembly and injection before and after initialization, e.g. injecting values for certain fields via reflection.
@Component
public class AutowireBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = ().getDeclaredFields();
        for (Field field : fields) {
            if (()) {
                (true);
                try {
                    (bean, "Injected Value");
                } catch (IllegalAccessException e) {
                    throw new BeansException("Failed to autowire field: " + (), e) {};
                }
            }
        }
        return bean;
    }

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

@Retention()
public @interface AutowireCustom {
}

public class MyBean {
    @AutowireCustom
    private String customField;

    public MyBean() {
    }

    @Override
    public String toString() {
        return "MyBean{customField='" + customField + "'}";
    }
}

InstantiationAwareBeanPostProcessor

present (sb for a job etc)

This interface inherits theBeanPostProcessorinterface, as InstantiationAwareBeanPostProcessor also belongs to the bean-level post processor, with the following differences:

BeanPostProcessinterface onlyExtension at bean initialization stage(before and after injection into the spring context), and theInstantiationAwareBeanPostProcessorThe interface adds 3 methods to this, increasing the scope of extensibility to include an instantiation phase and a property injection phase.

The main extension points of the class are the following six methods, two of which are extensions of BeanPostProcessor, mainly in the two main phases of the bean lifecycle:instantiation phasecap (a poem)initialization phase, illustrated together below, in the order in which they are called:

  • postProcessBeforeInstantiation: Called before the bean is instantiated, if it returns null, everything is executed in the normal order; if it returns an object that is an instance, then thepostProcessAfterInstantiation()will be executed and other extension points will no longer be triggered.
  • postProcessAfterInstantiation: Called after the bean has been instantiated, it allows further customization of the instantiated bean.
  • postProcessPropertyValues(method deprecated after spring 5.1): the bean has been instantiated and the phase is triggered during property injection.@Autowired@Resourceand other annotation principles are implemented based on this method; you can modify the value of a bean's attribute or perform other customized operations, theExecuted only when postProcessAfterInstantiation returns true.
  • postProcessBeforeInitialization(an extension of BeanPostProcessor): before initializing the bean, equivalent to injecting the bean into the spring context before; can be used to create a proxy class, if the return is not null (that is, the return of a proxy class), then the subsequent will only call the postProcessAfterInitialization() method
  • postProcessAfterInitialization(an extension of BeanPostProcessor): after initializing the bean, it is equivalent to injecting the bean into the spring context; the return value affects whether postProcessProperties() is executed or not, if it returns false, it is not executed.
  • postProcessProperties(): Called before the bean sets its attributes; used to modify the bean's attributes, if the return value is not null, then the value of the specified field will be changed

Note: InstantiationAwareBeanPostProcessor and BeanPostProcessor can be implemented at the same time and will work at the same time, but InstantiationAwareBeanPostProcessor'sThe execution timing is slightly earlier than the BeanPostProcessor.; specifically, look at the call sequence diagram above

InstantiationAwareBeanPostProcessor Provides finer-grained control for inserting custom logic into the bean instantiation and property setting process. Whether it's replacing the default instantiation process, controlling dependency injection, or modifying property values, theInstantiationAwareBeanPostProcessor Both provide great flexibility and extensibility, allowing developers to have finer control over the lifecycle of a bean.

Usage Scenarios

  1. Replace Bean before Instantiation: Replaces the default bean instantiation process, possibly by returning a proxy object.
@Component
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == ) {
            ("Replacement before instantiation Bean: " + beanName);
            Enhancer enhancer = new Enhancer();
            (beanClass);
            (new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    ("invoke a method: " + ());
                    return (obj, args);
                }
            });
            return ();
        }
        return null;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ("initialized Bean: " + beanName);
        return bean;
    }
}
  1. Control the dependency injection process after instantiation: perform some custom logic after instantiation but before dependency injection.
@Component
public class DependencyInjectionControlPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            ("Controlling dependency injection after instantiation: " + beanName);
            return false; // No default dependency injection
        }
        return true;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ("initialized Bean: " + beanName);
        return bean;
    }
}
  1. Modify attribute values: intervene during the attribute value setting process to modify or add attribute values.
@Component
public class PropertyModificationPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            ("Before setting the value of a property: " + beanName);
            // Logic for Modifying Attribute Values
        }
        return pvs;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ("initialized Bean: " + beanName);
        return bean;
    }
}

SmartInstantiationAwareBeanPostProcessor

present (sb for a job etc)

SmartInstantiationAwareBeanPostProcessor The most obvious difference with other extension points is that there are not many opportunities to apply them in actual business development scenarios, mainly within Spring.

This extended interface has 3 trigger point methods:

  • predictBeanType: This trigger point occurs atpostProcessBeforeInstantiationBefore (that is, before the InstantiationAwareBeanPostProcessor's method, which is not labeled on the diagram because theThere is generally less need to extend this point), this method is used to predict the type of the bean, returning the type of the first Class that was predicted successfully, or null if it couldn't be predicted; when calling the(name)This callback method is called when the bean type information is not available from the bean name to determine the type information.
  • determineCandidateConstructors: This trigger point occurs atpostProcessBeforeInstantiationAfter that, it is used to decide which constructor to use to construct the bean, and returns a list of all constructors for the bean; if not specified, it defaults to null, the uninstrumented constructor method for the bean. The user can extend this point to customize the selection of the appropriate constructor to instantiate the bean.
  • getEarlyBeanReference: This trigger point occurs atpostProcessAfterInstantiationAfter that, it is mainly used for Spring circular dependency problem solving, if circular dependency is not detected in Spring, this method will not be called; when there exists Spring circular dependency in this case, when the bean is instantiated, in order to prevent circular dependency, it will expose the callback method in advance, which is used for bean instantiation post-processing, and it will be executed after the InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation method is triggered after the execution;

Note: As with InstantiationAwareBeanPostProcessor, since SmartInstantiationAwareBeanPostProcessor is a subclass of InstantiationAwareBeanPostProcessor, it also extends the methods of InstantiationAwareBeanPostProcessor. Since SmartInstantiationAwareBeanPostProcessor is a subclass of InstantiationAwareBeanPostProcessor, it also extends all the methods of InstantiationAwareBeanPostProcessor. However, if there are two classes that override the methods of SmartInstantiationAwareBeanPostProcessor and InstantiationAwareBeanPostProcessor respectively, then theMethods of classes that override InstantiationAwareBeanPostProcessor precede methods of classes that override SmartInstantiationAwareBeanPostProcessor.(Note that it says here that there is a way to do both).

Usage Scenarios

  1. Custom Constructor Selection: Select a specific constructor when instantiating the bean.
@Component
public class CustomConstructorSelectionPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == ) {
            ("Selecting a custom constructor: " + beanName);
            try {
                return new Constructor<?>[] { () };
            } catch (NoSuchMethodException e) {
                throw new BeansException("The specified constructor could not be found", e) {};
            }
        }
        return null;
    }
}

public class MyBean {
    private String name;

    public MyBean() {
         = "Default Name";
    }

    public MyBean(String name) {
         = name;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }
}
  1. Solving circular dependencies: Solve circular dependencies by providing references to earlier beans.
@Component
public class EarlyBeanReferencePostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            ("Access to Early Bean quote: " + beanName);
            Enhancer enhancer = new Enhancer();
            (());
            (new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    ("invoke a method: " + ());
                    return (obj, args);
                }
            });
            return ();
        }
        return bean;
    }
}
  1. Predicted Bean Type: Predicts the type of a bean before it is instantiated.
@Component
public class BeanTypePredictionPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == ) {
            ("anticipate Bean typology: " + beanName);
            return ;
        }
        return null;
    }
}

MergedBeanDefinitionPostProcessor

present (sb for a job etc)

MergedBeanDefinitionPostProcessor inherits from BeanPostProcessor and can be called after instantiation, as long as it collects attributes of the bean, such as fields or methods marked with certain annotations. MergedBeanDefinitionPostProcessor.

For bean definitions imported in different ways, if there is a duplicate definition of the same bean, it will be based on whether or not the allowBeanDefinitionOverriding property is set to true, to determine whether or not to allow the overriding of bean definitions, and if not, an exception will be thrown. And before the bean is instantiated, the normalization of the beanDefinition type will be performed, i.e. mergeBeanFintion, which is converted to RootBeanfintion for subsequent processing. Of course, merge here refers more to the merger of parent and child bean definitions.

It is also used to collect annotations on beans, such as the common @Value, @NacosValue, @Mapper, etc., and then cache the collected data in injectionMetadataCache for subsequent use in, for example, property injection.

The interface has two extension methods:

  • postProcessMergedBeanDefinition: This method combines multiple bean definitions in Spring into oneRootBeanDefinitionis called after, but before, the bean is instantiated. The main purpose is to give the developer the opportunity to further customize and tweak the bean definition after it has been merged. The usage scenarios are as follows:
    • Custom Annotation Processing: Handles custom annotations and applies them to bean definitions.
    • Attribute modification: Adjust or set default values for certain properties in the bean definition before the bean is instantiated.
  • resetBeanDefinition: This method is called when a bean definition is reset. It is typically used to clean up or reset the state or cache associated with a particular bean definition. The usage scenarios are as follows:
    • Status Cleanup: Clears the cache or temporary state so that the bean definition can be re-parsed.
    • Reset custom metadata: Resets custom metadata or state when the bean definition is reset.

Usage Scenarios

  1. Modify the definition information of the merged bean: Modify the definition information of the bean before it is instantiated, for example, by adding a property value or modifying a constructor parameter.
@Component
public class CustomMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanType == ) {
            ("integrated Bean Defining Information, Bean name (of a thing): " + beanName);
            // 修改integrated Bean Defining Information
            ().add("name", "修改后的name (of a thing)");
        }
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        ("reprovision Bean Defining Information, Bean name (of a thing): " + beanName);
        // 实现reprovision逻辑
    }

}

public class MyBean {
    private String name;

    public void setName(String name) {
         = name;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }
}

  1. Implementing Generic Custom Logic: Performs some generic custom logic before all beans are instantiated.
@Component
public class CommonLogicMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        ("Processing of consolidated Bean Defining Information, Bean name (of a thing): " + beanName);
        // Add generic customization logic,For example, give all Bean Adding a property
        ().add("commonProperty", "Common Attribute Values");
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        ("reprovision Bean Defining Information, Bean name (of a thing): " + beanName);
        // 实现reprovision逻辑
    }

}

public class MyBean {
    private String name;
    private String commonProperty;

    public void setName(String name) {
         = name;
    }

    public void setCommonProperty(String commonProperty) {
         = commonProperty;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "', commonProperty='" + commonProperty + "'}";
    }
}
  1. Conditional Reset of Bean Definition Information: Resets the definition information of a bean under certain conditions so that the next instantiation can use the updated definition information.
@Component
public class ConditionalResetMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        ("Processing of consolidated Bean Defining Information, Bean name (of a thing): " + beanName);
        // Here you can decide whether or not to modify it depending on the conditions Bean define
        if (("conditionalBean")) {
            ().add("name", "reprovision后的name (of a thing)");
        }
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        ("reprovision Bean Defining Information, Bean name (of a thing): " + beanName);
        // 这里可以实现条件性reprovision逻辑
    }
}

public class ConditionalBean {
    private String name;

    public void setName(String name) {
         = name;
    }

    @Override
    public String toString() {
        return "ConditionalBean{name='" + name + "'}";
    }
}

BeanNameAware

present (sb for a job etc)

This class is a type of Aware extension where the trigger point is before the initialization of the bean, which is thepostProcessBeforeInitializationPreviously, there was only one trigger point method for this class:setBeanName

is used to let the bean get its name in the Spring container. Implements theBeanNameAware A bean of an interface can be initialized with its own bean name, which is useful in scenarios where you need to do logical processing based on the name of the bean.

Usage Scenarios

  1. Record or log the bean name: In some application scenarios, developers may want to record or log the name of the bean when it is initialized. This is useful for debugging and logging.
@Component
public class LoggingBean implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
         = name;
        ("set up Bean name (of a thing): " + name);
    }

    public void doSomething() {
        ("Certain operations are being performed., be facing (us) Bean name (of a thing): " + beanName);
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        LoggingBean loggingBean = ();
        ();
    }
}
  1. Implementing Conditional Logic Based on Bean Name: Sometimes a bean may need to decide to perform different logic based on its name. For example, you can perform specific actions based on the name of the bean in the initialization process or in certain method calls.
@Component
public class ConditionalLogicBean implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
         = name;
        ("set up Bean name (of a thing): " + name);
    }

    public void performAction() {
        if ("conditionalLogicBean".equals(beanName)) {
            ("Implementing Specific Logic, Because it is. conditionalLogicBean");
        } else {
            ("Execution of common logic");
        }
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ConditionalLogicBean conditionalLogicBean = ();
        ();
    }
}
  1. Dynamically registering multiple beans of the same type: In some complex application scenarios, it may be necessary to dynamically register multiple beans of the same type and differentiate them by name. ImplementationsBeanNameAware interface makes it easy to get and use the names of these beans.
@Component("beanA")
public class DynamicBeanA implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
         = name;
        ("set up Bean name (of a thing): " + name);
    }

    public void execute() {
        ("fulfillment Bean: " + beanName);
    }
}

@Component("beanB")
public class DynamicBeanB implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
         = name;
        ("set up Bean name (of a thing): " + name);
    }

    public void execute() {
        ("fulfillment Bean: " + beanName);
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        DynamicBeanA beanA = (DynamicBeanA) ("beanA");
        DynamicBeanB beanB = (DynamicBeanB) ("beanB");
        ();
        ();
    }
}

BeanClassLoaderAware

present (sb for a job etc)

This is used to allow a bean to get theClassLoaderThe Bean implementing this interface is injected after its properties are set and before the initialization method is called. Beans that implement this interface are injected after their properties are set and before the initialization method is called.ClassLoader. The interface defines a method:

  • void setBeanClassLoader(ClassLoader classLoader): In some scenarios where classes need to be loaded dynamically, getting theClassLoader is very useful.

Usage Scenarios

  1. Dynamically loading classes: Sometimes, we may need to load classes dynamically at runtime, using theBeanClassLoaderAware It is easy to get theClassLoader to fulfill this need.
@Component
public class DynamicClassLoader implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
         = classLoader;
        ("Class loader set");
    }

    public void loadClass(String className) {
        try {
            Class<?> clazz = (className);
            ("Loaded Classes:" + ());
        } catch (ClassNotFoundException e) {
            ("Class not found:" + className);
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = (, args);
        DynamicClassLoader dynamicClassLoader = ();
        ("");
        ("non-existent class");
    }
}
  1. Checking class availability: In some cases, we may need to check if a class is available in the current class path. Utilizing theBeanClassLoaderAware This need can be easily realized.
@Component
public class ClassAvailabilityChecker implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
         = classLoader;
        ("Class loader set");
    }

    public boolean isClassAvailable(String className) {
        try {
            Class<?> clazz = (className);
            ("class available:" + ());
            return true;
        } catch (ClassNotFoundException e) {
            ("class unavailability:" + className);
            return false;
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = (, args);
        ClassAvailabilityChecker checker = ();
        ("");
        ("non-existent class");
    }
}
  1. Load resource files: viaBeanClassLoaderAware acquiredClassLoader, we can also easily load resource files.
@Component
public class ResourceLoader implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
         = classLoader;
        ("Class loader set");
    }

    public void loadResource(String resourcePath) {
        InputStream inputStream = (resourcePath);
        if (inputStream != null) {
            ("Resource loaded:" + resourcePath);
        } else {
            ("Resource not found:" + resourcePath);
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = (, args);
        ResourceLoader resourceLoader = ();
        ("");
        ("Non-existent resources");
    }
}

BeanFactoryAware

present (sb for a job etc)

This class has only one trigger point, which occurs after the bean is instantiated and before the property is injected, i.e. before the Setter. The extension point method for this class issetBeanFactoryYou can get it.BeanFactoryThis property allows for more complex bean operations. For example, dynamically fetching other beans, checking the state of a bean, and so on.

Usage Scenarios

  1. Get other beans dynamically: by implementing theBeanFactoryAware interface, a bean can dynamically fetch other beans at runtime, which is useful in scenarios where decoupling is required.
@Component
public class DynamicBeanFetcher implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
         = beanFactory;
        ("pour into BeanFactory an actual example");
    }

    public void fetchAndUseBean() {
        MyBean myBean = ();
        ("acquired Bean an actual example: " + myBean);
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "this is MyBean an actual example";
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        DynamicBeanFetcher fetcher = ();
        ();
    }
}
  1. Checking the state of a bean: via theBeanFactoryAwareThis allows you to check the existence or state of a bean at runtime, which is useful in scenarios where you need to check the state of a bean dynamically.
@Component
public class BeanStateChecker implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
         = beanFactory;
        ("pour into BeanFactory an actual example");
    }

    public void checkBeanState() {
        boolean exists = ("myBean");
        ("MyBean whether or not: " + exists);
    }
}

@Component("myBean")
public class MyBean {
    @Override
    public String toString() {
        return "this is MyBean an actual example";
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        BeanStateChecker checker = ();
        ();
    }
}
  1. Initialization Logic for Creating Complex Beans: In some complex business scenarios, it is sometimes necessary to perform some complex logic during bean initialization, such as dynamically creating other beans and injecting them into the current bean. This is accomplished through theBeanFactoryAware This can be accomplished.
@Component
public class ComplexBeanInitializer implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
         = beanFactory;
        ("pour into BeanFactory an actual example");
    }

    public void initializeComplexBean() {
        MyBean myBean = ();
        ("Initialization Complexity Bean, acquired MyBean an actual example: " + myBean);
        // Complex initialization logic can be performed here
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "this is MyBean an actual example";
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ComplexBeanInitializer initializer = ();
        ();
    }
}

ApplicationContextAwareProcessor

present (sb for a job etc)

The class itself does not have an extension point, but is a concrete implementation of the BeanPostProcessor extension interface, but there are six extension points that can be implemented within the class , which are triggered after the bean is instantiated and before it is initialized.

As you can see, this class is used to implement various driver interfaces, after the bean is instantiated and after the properties are populated. It has six internal extension points available for implementation, which are interfaces that Spring has reserved for focused extension implementations with Spring'sBean life cycle are closely related and are presented below in the order in which the extension points are invoked:

  • EnvironmentAware: Used to getEnviromentAwareAn extension of the class, this variable is very useful, you can get all the parameters of the system; in addition, you can also inject the way to get the Environment, which way you need to implement the scenario and decide. Of course, I personally think that this Aware is not necessary to extend, because spring can be injected directly through the way to get.

  • EmbeddedValueResolverAware: Used to getStringValueResolverAn extension class ofStringValueResolverIt is possible to get variables based on properties of type String; however, in general we use the@ValueThe way to get the properties variable is determined by the implementation scenario. If this Aware interface is implemented, it's a good idea to put theStringValueResolverCache it and go through this class to get theStringtype of variable, the effect is the same.

  • ResourceLoaderAware: Used to getResourceLoaderAn extension class ofResourceLoaderCan be used to get all resource objects within the classpath.

  • ApplicationEventPublisherAware: Used to getApplicationEventPublisherAn extension class ofApplicationEventPublishercan be used to publish events; this object can also be obtained by spring injection, in conjunction with theApplicationListenerto be used together, as described below inApplicationListenerIt will be mentioned in detail at the time.

  • MessageSourceAware: Used to getMessageSourceAn extension class ofMessageSourceMainly used for internationalization.

  • ApplicationContextAware: Used to getApplicationContextAn extension class ofApplicationContextIt is the spring context manager , you can manually get any bean registered in the spring context . more often than not, the practice is to extend this interface to cache the spring context , wrapped in static methods .
    at the same timeApplicationContextAlso realizedBeanFactoryMessageSourceApplicationEventPublisherand other interfaces can also be used to do things with related interfaces.

Usage Scenarios

  1. Get other beans dynamically: by implementing theApplicationContextAware interfaces, beans can dynamically fetch other beans at runtime, which is useful in some scenarios where decoupling is required.
@Component
public class DynamicBeanFetcher implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         = applicationContext;
        ("pour into ApplicationContext an actual example");
    }

    public void fetchAndUseBean() {
        MyBean myBean = ();
        ("acquired Bean an actual example: " + myBean);
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "this is MyBean an actual example";
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        DynamicBeanFetcher fetcher = ();
        ();
    }
}
  1. Using ApplicationContext for event publishing: In some scenarios, beans may need to publish events. This is accomplished by implementing theApplicationContextAware interface, which makes it easy to get theApplicationContext instance and publish the event.
@Component
public class EventPublisherBean implements ApplicationContextAware, ApplicationEventPublisherAware {

    private ApplicationContext applicationContext;
    private ApplicationEventPublisher eventPublisher;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         = applicationContext;
        ("pour into ApplicationContext an actual example");
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
         = eventPublisher;
    }

    public void publishCustomEvent(String message) {
        CustomEvent customEvent = new CustomEvent(this, message);
        (customEvent);
        ("Publishing custom events: " + message);
    }
}

public class CustomEvent extends ApplicationEvent {
    private String message;

    public CustomEvent(Object source, String message) {
        super(source);
         = message;
    }

    public String getMessage() {
        return message;
    }
}

@Component
public class CustomEventListener {

    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        ("Receiving custom events: " + ());
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        EventPublisherBean publisher = ();
        ("This is a custom event message");
    }
}
  1. Access to environmental information: through the realization ofApplicationContextAware interface, the bean can access theApplicationContextand get environment configuration information from it, such as reading property values from a configuration file.
@Component
public class EnvironmentAwareBean implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         = applicationContext;
        ("pour into ApplicationContext an actual example");
    }

    public void printEnvironmentProperty() {
        Environment environment = ();
        String propertyValue = ("");
        ("The value of the environment attribute read: " + propertyValue);
    }
}

@Configuration
@ComponentScan(basePackages = "")
@PropertySource("classpath:")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        EnvironmentAwareBean environmentAwareBean = ();
        ();
    }
}

@PostConstruct

present (sb for a job etc)

You can see that it is not a Spring-defined annotation itself, but Spring provides a specific implementation. This is not considered an extension point, in fact, it is an annotation. Its role is in the initialization phase of the bean, if a method labeled with the@PostConstruct, will call this method first. The point here is to focus on the standard trigger point for thispostProcessBeforeInitializationAfter that.Before.

Attention:

  • Methods marked with the @PostConstruct annotation cannot have parameters unless they are interceptors, which can take an InvocationContext object defined by the interceptor specification.
  • Methods marked with the @PostConstruct annotation cannot have a return value; in fact, if there is a return value, it will not report an error, but it will be ignored;
  • Methods marked with the @PostConstruct annotation cannot be modified by static, but final is allowed;

Usage Scenarios

The usage scenario is similar to InitializingBean, see below.

InitializingBean

present (sb for a job etc)

This class, as the name suggests, is also used to initialize beans.InitializingBeanThe interface provides a way to initialize methods for beans, it only provides an extension point after bean instantiation, property injectionafterPropertiesSetmethod, which is executed by any class that inherits this interface, before initialization and after property assignment. ThisTiming of triggering of extension pointsexistpostProcessAfterInitializationBefore.

Attention:

  • Similar effects to InitializingBean#afterPropertiesSet() areinit-methodHowever, it should be noted that InitializingBean#afterPropertiesSet() executes slightly before init-method;
  • InitializingBean#afterPropertiesSet() is called by literally calling the bean's afterPropertiesSet() during bean initialization;
  • The bean custom attribute init-method is called via java reflection ;

Usage Scenarios

  1. Initialize resources: you can automatically start some resources after the bean is initialized, such as database connection, file read, etc.
public class NormalBeanA implements InitializingBean{
    @Overrideimport ;
import ;

@Component
public class ResourceInitializer implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // Simulation resource initialization
        ("Resource initialization:Establishing a database connection");
    }

    public void performAction() {
        ("Resource utilization:Perform database operations");
    }
}
    
@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ResourceInitializer initializer = ();
        ();
    }
}
  1. Setting the initial value
@Component
public class InitialValueSetter implements InitializingBean {

    private String initialValue;

    @Override
    public void afterPropertiesSet() {
        initialValue = "default value";
        ("Setting the initial value:" + initialValue);
    }

    public void printValue() {
        ("current value:" + initialValue);
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        InitialValueSetter valueSetter = ();
        ();
    }
}
  1. Load Configuration: You can load the necessary configuration after the bean has been initialized, e.g. by reading the configuration from a file or database.
@Component
public class ConfigLoader implements InitializingBean {

    private String configValue;

    @Override
    public void afterPropertiesSet() {
        // Analog configuration loading
        configValue = "configuration value";
        ("Load Configuration:" + configValue);
    }

    public void printConfig() {
        ("current configuration:" + configValue);
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ConfigLoader configLoader = ();
        ();
    }
}

SmartInitializingSingleton

present (sb for a job etc)

There is only one method in this interfaceafterSingletonsInstantiatedThe role of the callback interface is to be called after the initialization of all the singleton objects (non-lazy-loaded objects) managed by the spring container. The trigger time ispostProcessAfterInitializationAfter.

Attention:

  • The scope of a bean implementing the SmartInitializingSingleton interface must be singleton for afterSingletonsInstantiated() to trigger;
  • afterSingletonsInstantiated() triggers the execution of theNon-lazily loaded singleton beanImplementations, property injections, and related initialization operations have been completed;
  • afterSingletonsInstantiated()The timing of the execution of theDefaultListableBeanFactory#preInstantiateSingletons();

Usage Scenarios

  1. Global Initialization Operations: You can perform some global initialization operations after all the singleton beans have been initialized, such as setting up caches, starting global scheduling tasks, and so on.
@Component
public class GlobalInitializer implements SmartInitializingSingleton {

    @Override
    public void afterSingletonsInstantiated() {
        // Simulating global initialization operations
        ("global initialization operation:Initiate global scheduling tasks");
    }
}
  1. Check System State: can be used to check the system state after all singleton beans have been initialized to ensure that the system is running in the expected state.

  2. Load Global Configuration: The global configuration can be loaded after all singleton beans are initialized, e.g. by reading the configuration from a file or database and applying it to the system.

FactoryBean

present (sb for a job etc)

Typically, Spring passes thereflex mechanismUse the class attribute of the bean to specify the branch class to instantiate the bean, in some cases, the process of instantiating the bean is more complex, if you follow the traditional way, you need to provide a lot of configuration information in the bean.Spring provides afactory class interface, users can customize the logic of instantiating beans by implementing the interface.FactoryBeanInterfaces hold an important place for the Spring Framework, and Spring itself provides more than 70FactoryBeanimplementations. They hide the details of instantiating some of the complex beans to the higher level applications.

Trigger: For example, when other framework technologies are integrated with Spring, such as mybatis and Spring integration, mybatis is through the SqlSessionFactory to create a Sqlsession to perform sql, then the Service layer in the call to the Dao layer interface to perform database operations certainly have to hold the SqlSessionFactory, then the question arises: how can the Spring container hold SqlSessionFactory? The answer is SqlSessionFactoryBean, which implements the FactoryBean interface.

Difference between FactoryBean and BeanFactory

  • The FactoryBean interface has three methods:
    • getObject(): used to get the bean, the mainTo be applied in scenarios where some complex beans are created
    • getObjectType(): return the type of the Bean instance created by this factory.
    • isSingleton(): used to determine whether the returned bean belongs to a singleton, the default trure, in common parlance, is a factory bean;
  • BeanFactory is the root interface of the Spring bean container , ApplicationContext is a high-level interface of the Spring bean container , inherited from the BeanFactory, a common understanding is to generate bean factory;

Usage Scenarios

  1. Creating complex objects: using theFactoryBean can help us create objects that require complex configuration or initialization.
class ComplexObject {
    private String name;
    private int value;

    public ComplexObject(String name, int value) {
         = name;
         = value;
    }

    @Override
    public String toString() {
        return "ComplexObject{name='" + name + "', value=" + value + "}";
    }
}

@Component
public class ComplexObjectFactoryBean implements FactoryBean<ComplexObject> {

    @Override
    public ComplexObject getObject() {
        // Creating Complex Objects
        ComplexObject complexObject = new ComplexObject("complex object", 42);
        ("Creating Complex Objects:" + complexObject);
        return complexObject;
    }

    @Override
    public Class<?> getObjectType() {
        return ;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ComplexObject complexObject = ();
        ("获取complex object:" + complexObject);
    }
}
  1. Dynamic switching implementation: Assuming we need to dynamically switch the implementation class of a bean based on certain conditions, we can use theFactoryBean
interface Service {
    void execute();
}

class ServiceImplA implements Service {
    @Override
    public void execute() {
        ("Implementation service realizationA");
    }
}

class ServiceImplB implements Service {
    @Override
    public void execute() {
        ("Implementation service realizationB");
    }
}

@Component
public class DynamicServiceFactoryBean implements FactoryBean<Service> {

    private boolean useServiceA = true; // Can be set dynamically by configuration or condition

    @Override
    public Service getObject() {
        if (useServiceA) {
            ("Creating a Service ImplementationA");
            return new ServiceImplA();
        } else {
            ("Creating a Service ImplementationB");
            return new ServiceImplB();
        }
    }

    @Override
    public Class<?> getObjectType() {
        return ;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        Service service = ();
        ();
    }
}
  1. Delayed initialization:FactoryBean can be used to delay the initialization of certain beans.Instantiate only on first fetch
class LazyObject {
    public LazyObject() {
        ("Lazy objects are created");
    }

    public void doSomething() {
        ("lazy object executes an operation");
    }
}

@Component
public class LazyObjectFactoryBean implements FactoryBean<LazyObject> {

    @Override
    public LazyObject getObject() {
        ("Creating lazy object instances");
        return new LazyObject();
    }

    @Override
    public Class<?> getObjectType() {
        return ;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

@Configuration
@ComponentScan(basePackages = "")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext();
        ("Before getting a lazy object instance");
        LazyObject lazyObject = ();
        ("After getting the lazy object instance");
        ();
    }
}

CommandLineRunner and ApplicationRunner

present (sb for a job etc)

These two are new extensions in Springboot, the reason why these two extensions together, because it is highly similar to the two features, the difference is only the name, the extension of the method shape parameter types, the implementation of a number of small differences in the sequence.

These two interfaces are triggered when the entire project has been started and are executed automatically. If there are multipleCommandLineRunnerThe program can be utilized@Orderto sort.

Attention:

  • Both CommandLineRunner and ApplicationRunner have an extension method run(), but the type of the run() shape parameter is different;
  • The formal parameter type of the () method is String... args, and the formal parameter type of () is ApplicationArguments args;
  • () is executed a little later than ();
  • CommandLineRunner and ApplicationRunner trigger the execution of the timing is in the Spring container, Tomcat container formally started after the completion of the business request can be formally processed before the last step of the project startup;
  • Scenarios where CommandLineRunner and ApplicationRunner can be applied: pre-loading of hot data, clearing temporary files, reading custom configuration information, etc. before project startup;

Usage Scenarios

  1. Initialize the data: use theCommandLineRunner It is possible to initialize some necessary data after the application is started, such as loading certain configurations from the database or inserting initial data.
@Component
public class DataInitializer implements CommandLineRunner {

    @Override
    public void run(String... args) {
        ("Initialize data: Insert initial data");
        // Simulate inserting initial data
        insertInitialData();
    }

    private void insertInitialData() {
        ("Inserting data: user table initial data"); }
    }
}
  1. Execute the task after startup: use theCommandLineRunner It is possible to perform some specific tasks after the application is launched, such as sending a notification or starting some background tasks.
@Component
public class TaskExecutor implements CommandLineRunner {

    @Override
    public void run(String... args) {
        ("Execute tasks after startup:Send startup notification");
        // 模拟Send startup notification
        sendStartupNotification();
    }

    private void sendStartupNotification() {
        ("notifications:Application launched");
    }
}
  1. Read command line arguments: useCommandLineRunner Command line arguments can be fetched and processed, which is useful for scenarios where an application needs to be dynamically configured based on startup parameters.
@Component
public class CommandLineArgsProcessor implements CommandLineRunner {

    @Override
    public void run(String... args) {
        ("Handling command line arguments:");
        for (String arg : args) {
            ("parameters:" + arg);
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        (, new String[]{"parameters1", "parameters2", "parameters3"});
    }
}

ApplicationListener and ApplicationContextInitializer

present (sb for a job etc)

To be precise, this should not be considered an extension point in spring&springboot.ApplicationListenerthat can listen to an eventeventThe trigger timing can be interspersed during the execution of a business method, and the user can customize a business event. But spring also has some internal built-in events, this kind of event, can be interspersed in the start call. We can also use this feature to do some of their own built-in event listener to achieve and some of the previous trigger point roughly the same thing.

Next is a list of spring's main built-in events:

  • ContextRefreshedEvent
    This event is posted when the ApplicationContext is initialized or refreshed. This can also be done in theConfigurableApplicationContextinterface using therefresh()method to occur. Initialization in this context means: all beans are successfully loaded, post-processing beans are detected and activated, all Singleton beans are pre-instantiated, theApplicationContextThe container is ready for use.
  • ContextStartedEvent
    When using theConfigurableApplicationContext (The start() method in the (ApplicationContext sub-interface) interface starts theApplicationContextWhen the event is posted. You can investigate your database, or you can restart any stopped applications after receiving this event.
  • ContextStoppedEvent
    When using theConfigurableApplicationContextinterfacestop()cessationApplicationContextwhen posting this event. You can do the necessary cleanup work after receiving this event
  • ContextClosedEvent
    When using theConfigurableApplicationContextinterfaceclose()Method closureApplicationContext The event is posted when the A closed context reaches the end of its lifecycle; it cannot be refreshed or restarted
  • RequestHandledEvent
    This is a web-specific event that tells all bean HTTP requests that have been served. It can only be applied to web applications that use DispatcherServlet. When using Spring as the front-end MVC controller, this event is automatically triggered when Spring finishes processing the user request

Usage Scenarios

  • Listening to custom events: Use theApplicationListener Custom events can be listened to and handled.
// Defining custom events
class CustomEvent extends ApplicationEvent {
    private final String message;

    public CustomEvent(Object source, String message) {
        super(source);
         = message;
    }

    public String getMessage() {
        return message;
    }
}

// Listening to custom events
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {

    @Override
    public void onApplicationEvent(CustomEvent event) {
        ("Listening to custom events:handling of incidents");
        handleCustomEvent(event);
    }

    private void handleCustomEvent(CustomEvent event) {
        ("Handling custom events:" + ());
    }
}

@Component
public class EventPublisher implements ApplicationEventPublisherAware {
    
    private ApplicationEventPublisher eventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
         = eventPublisher;
    }

    public void publishCustomEvent(final String message) {
        ("Publishing custom events:" + message);
        CustomEvent customEvent = new CustomEvent(this, message);
        (customEvent);
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = (, args);
        EventPublisher publisher = ();
        ("This is the message for the custom event");
    }
}

@PreDestroy

present (sb for a job etc)

@PreDestroy together with@PostConstructLike, is an annotation in Java EE that is used to execute a specific method before the Spring container destroys the bean. This annotation is commonly used for operations such as freeing resources, closing connections, and cleaning up the cache. It is similar to the@PostConstruct Similarly.@PreDestroy annotation's methods are called before the bean is destroyed.

Usage Scenarios

The usage scenario is similar to DisposableBean, see below.

DisposableBean

present (sb for a job etc)

There is also only one way to do this extension point:destroy()This method is automatically executed when this object is destroyed and the Spring container is closed. For example, runningThis method is triggered when the This extension point is largely unused

Attention:

  • DisposableBean is an interface that provides a way for Spring beans to release resources , with only one extension method, destroy();
  • Implement the DisposableBean interface and override destroy() to get a callback when the Spring container destroys the bean;
  • The execution timing of the destroy() callback is when the Spring container is closed and all beans need to be destroyed;
  • Similar to InitializingBean, InitializingBean#afterPropertiesSet() is triggered when the bean is initialized and DisposableBean#destroy() is triggered when the bean is destroyed.

Usage Scenarios

  • Free database connections and clean up temporary files: When the application is shut down, database connections are freed to ensure that resources are properly reclaimed and temporary files are deleted to ensure that disk space is properly freed.
@Component
public class DatabaseConnectionManager implements DisposableBean {

    @Override
    public void destroy() {
        ("Releasing a database connection:Close connection");
        // Simulate closing a database connection
        closeConnection();
    }

    private void closeConnection() {
        ("Database connection closed");
    }
}

summarize

We can roughly peek among these spring&springboot extension points throughout theLife cycle of a bean. In business development or when writing middleware business, you can make reasonable use of the extension points provided to us by spring to do something within the various phases of spring startup. To achieve the purpose of custom initialization.

I am also in the continuous learning, so this summary if there are errors or omissions, please comment or contact me for correction, this article will continue to iterate in the~

About the Author.

From the first-line programmer Seven's exploration and practice, continuous learning iteration in the~

This article is included in my personal blog:https://

Public number: seven97, welcome to follow~