Read the official document together ---- spring IOC (07)

1.8. Container extension point

ApplicationContext does not need to be subclassed by developers. Instead, you can extend the Spring IoC container by inserting an implementation of a special integration interface. The following sections describe these integration interfaces.

1.8.1. Custom beans implement the BeanBeanPostProcessor interface

The BeanPostProcessor interface defines callback methods. You can implement these callback methods to modify the default bean instantiation logic, dependency resolution logic, etc.
If you want to implement some custom logic after the Spring container completes instantiation, configuration and initialization of beans, you can insert one or more custom beanpostprocessors.

You can configure multiple BeanPostProcessor instances, and you can control the running order of these instances by implementing the Ordered interface and setting the order attribute.

@Component
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
BeanPostProcessor Instance operation is bean Examples of.
in other words, Spring IoC Container instantiates a bean example,
Then use BeanPostProcessor Process these examples.

BeanPostProcessor Instances are scoped by container.  
This makes sense only when using a container hierarchy.
If BeanPostProcessor Define a in a container, which will only apply to the bean After treatment.
In other words, defined in a container bean Will not be BeanPostProcessor Post process the definition in another container,
This is true even if the two containers are part of the same hierarchy.

BeanPostProcessor The modification is bean Content after instantiation,
If you want to change the actual bean Definition (i.e bean definition)
You need to use BeanFactoryPostProcessor Interface.

org. springframework. beans. factory. config. The beanpostprocessor interface consists of exactly two callback methods.
When this class is registered as the container's post processor, for each bean instance created by the container, the post processor will be called after any bean is instantiated and before the container initialization method (such as initializingbean.afterpropertieset() or any declared init method) is used.
The post processor can perform any operation on the bean instance or ignore the callback completely.
The post processor usually checks the callback interface or can wrap the Bean with a proxy.
Some Spring AOP infrastructure classes are implemented as post processors to provide proxy wrapper logic.

ApplicationContext Automatic detection implementation BeanPostProcessor Interface all bean,Note to register as bean,Just implementing the interface is not possible.

Please note that when declaring the BeanPostProcessor by using the @ bean factory method, the return type of the factory method should be the implementation class itself or at least org springframework. beans. factory. config. BeanPostProcessor interface to clearly indicate the post processor nature of the bean.
Otherwise, ApplicationContext cannot automatically detect it by type before it is fully created.
Since BeanPostProcessor needs to be instantiated in advance in order to be applied to the initialization of other beans in the context, this early type detection is crucial.

    @Bean
    public BeanPostProcessor myBeanPostProcessor(){
        return new MyBeanPostProcessor();
    } 
Register programmatically BeanPostProcessor example
 Although recommended BeanPostProcessor The registration method is through ApplicationContext Automatic detection,
But you can ConfigurableBeanFactory use addBeanPostProcessor Methods register them programmatically.

When you need to evaluate the conditional logic before registration (for example, the application scenario is xxx Conditions before registration, xxx When the condition is not registered),
Even context replication across hierarchies is required Bean post-processor This will be very useful when.

But please note that BeanPostProcessor Instances added programmatically do not comply with this rule Ordered Interface.
Here, the order of registration determines the order of execution.
Also note that in order to BeanPostProcessor Instances registered programmatically are always processed before automatically detecting registered instances,
Regardless of any clear order.
BeanPostProcessor Examples and AOP Automatic proxy
 realization BeanPostProcessor Classes of interfaces are special, and containers treat them differently.
BeanPostProcessor They directly reference all instances and bean It will be instantiated at startup,
As ApplicationContext Part of a special start-up phase.

next, BeanPostProcessor Register all instances in a sorted manner and apply them to all other instances in the container bean. 
But because AOP The implementation of automatic agent is through BeanPostProcessor Interface,
So in AOP of BeanPostProcessor Before interface instantiation
BeanPostProcessor Instance or BeanPostProcessor Directly referenced by the instance bean Are not eligible for automatic proxy.
And for any such bean There is no way to deal with the section BeanPostProcessor Point to them.
You should see a informational log message:
Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying). 
This message probably means this bean Didn't get everything BeanPostProcessor Treatment of

Let's analyze the logic of this log: we don't need it AOP of BeanPostProcessor use AutowiredAnnotationBeanPostProcessor Look at the situation

First of all, this log is in BeanPostProcessorChecker Class,
The class itself is implemented BeanPostProcessor,
Spring Add this to the container processor The codes are as follows:
    
        //Get all beans of BeanPostProcessor type 
        //The first true indicates that non singleton bean s are included
        //The second false indicates that only the beans that have been instantiated are found. If they are factory beans, they will not be included
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
        //Number of all post processors in the current beanFactory + 1 + number of postbeannames
        //This quantity will be judged later
        //beanFactory.getBeanPostProcessorCount() system built-in processor
        //1 is the BeanPostProcessorChecker
        //postProcessorNames.length is the processor that can be scanned
        //The sum of this quantity is all the processor s that the system can see at present
        //Another possibility is that after parsing some bean s, a processor is added, which is not included 
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        
        //add BeanPostProcessorChecker enters beanPostProcessor chain
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

BeanPostProcessorChecker The method to judge and print the log above is as follows:
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            //If the current bean is not an instance of postProcessor
            //And not an internal bean
            //And this beanFactory. Getbeanpostprocessorcount() is less than the value just added
            //Only when all three are satisfied will the log be printed
            if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
                if (logger.isInfoEnabled()) {
                    logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
                            "] is not eligible for getting processed by all BeanPostProcessors " +
                            "(for example: not eligible for auto-proxying)");
                }
            }
            return bean;
        }

        //If the current beanName is not empty and the corresponding bean is a bean used inside the container, return true    
        private boolean isInfrastructureBean(@Nullable String beanName) {
            if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
                BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
                return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE);
            }
            return false;
        }
        
Watching Spring createBean Time traversal postProcessor Code of
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }
It is through such a loop that the post method is executed applyBeanPostProcessorsAfterInitialization,The same is true of the pre method

Now suppose we have a custom beanPostProcessor We need to inject a custom beanA,
So in beanPostProcessor When it is instantiated, it will be required to inject our customized beanA,
So now there are many situations:
    1.We use it set Or the constructor injects that beanA Will be instantiated and injected
    2.If we use@Autowired,When we customize beanPostProcessor instantiation 
    stay AutowiredAnnotationBeanPostProcessor Before instantiation, then beanA Can't be injected
    If after, the value can still be injected
    However, this line of log will be printed in both cases
Bean 'beanA' of type [org.springframework.beanA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

The following example shows how to write, register, and use BeanPostProcessor instances in ApplicationContext.

Example: Hello World, beanpostprocessor style

The first example demonstrates the basic usage. The example shows a custom BeanPostProcessor implementation that calls the toString() method of each bean when the container creates it and prints the result string to the system console.

The following listing shows the custom BeanPostProcessor implementation class definition:

package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // Just return the instantiated bean as is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // We can return any object reference
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}

The following beans elements use instantiaontracingbeanpostprocessor:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        https://www.springframework.org/schema/lang/spring-lang.xsd">

    <lang:groovy id="messenger"
            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>

    <!--
   When above bean (messenger)When instantiated, this custom BeanPostProcessor Implement the output of facts to the system console   -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

Note how the postprocessor is instantiated. It doesn't even have a name, and because it's a bean, it can do dependency injection like other beans.

The following Java application runs the previous code and configuration:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = ctx.getBean("messenger", Messenger.class);
        System.out.println(messenger);
    }

}

The output of the previous application is similar to the following:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Example: RequiredAnnotationBeanPostProcessor

Combining callback interfaces or annotations with custom BeanPostProcessor implementations is a common way to extend the Spring IoC container.

An example is Spring's Autowired annotation BeanPostProcessor, a BeanPostProcessor implementation that comes with the Spring distribution, which ensures that annotated properties (@ Autowired, @ Value, @ Inject, etc.) will be injected into a bean instance.

1.8.2. Custom configuration metadata beanfactoryprocessor

The next extension point we want to see is org springframework. beans. factory. config. BeanFactoryPostProcessor.

The main difference between this interface and BeanPostProcessor is that BeanFactoryPostProcessor operates on Bean configuration metadata.
That is, the Spring IoC container allows beanfactoryprocessor to read the configuration metadata, and it is possible to change the metadata before the container instantiates any beans.

You can configure multiple beanfactoryprocessor instances, and you can set the order property to control the running order of these instances. However, this property can only be set if beanfactoryprocessor implements the Ordered interface.

If you want to change the actual bean example(Objects created from configuration metadata),You need to use BeanPostProcessor. 
Although in BeanFactoryPostProcessor Used in bean The example is technically feasible(For example, by using BeanFactory.getBean()),
But doing so can lead to premature bean Instantiation, which violates the standard container life cycle.
This can lead to negative side effects, such as bypassing bean Post processing of.

In addition, BeanFactoryPostProcessor The scope of the instance is each container.
This is useful only when using container hierarchies.
If you define in a container BeanFactoryPostProcessor,Then it only applies to the bean definition.
In a container Bean The definition will not be defined by a in another container BeanFactoryPostProcessor Instance, even if the two containers belong to the same hierarchy.

When the beanfactoryprocessor is declared in the ApplicationContext, it runs automatically to apply changes to the configuration metadata that defines the container.
Spring includes a number of predefined bean factory postprocessors, such as PropertyOverrideConfigurer and PropertySourcesPlaceholderConfigurer.
You can also use a custom beanfactoryprocessor, for example, to register a custom attribute editor.

ApplicationContext automatically detects any beans deployed that implement the beanfactoryprocessor interface. When appropriate, these beans will be used by bean factory post processors.

You can also deploy these custom bean factory post processors like any other bean.

Example: PropertySourcesPlaceholderConfigurer

You can externalize the property values in the bean definition into a separate file using the standard Java property format using the PropertySourcesPlaceholderConfigurer. In this way, the person deploying the application can customize environment specific properties, such as database url and password, without modifying the complexity or risk of the main XML definition file or container file.

Consider the following xml based configuration metadata fragment that defines a data source with placeholder values:

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

This example shows Properties configured from an external Properties file.
At run time, apply PropertySourcesPlaceholderConfigurer to replace metadata for some properties of the data source. Specify the value to be replaced as a placeholder for the form ${property name} that follows Ant and log4j and JSP EL styles.

The actual value comes from another file in the standard Java Properties format:

jdbc.driverClassName = org.hsqldb.jdbcDriver
jdbc.url = jdbc:hsqldb:hsql://production:9002
jdbc.username = sa
jdbc.password = root

Therefore, ${jdbc.username} replaces the string with the value "sa" at run time, and other placeholder values that match the keys in the properties file also apply.
Check the property placeholders defined for most properties and bean s in PropertySourcesPlaceholderConfigurer. In addition, you can customize placeholder prefixes and suffixes.

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
        //Custom Prefix suffix
        <property name="placeholderPrefix" value="${"/>
        <property name="placeholderSuffix" value="}"/>
    </bean>
1.8.3. Custom instantiation logic FactoryBean

You can go to www.org.org springframework. beans. factory. Factorybeans implement interfaces for objects that are themselves factories.

The FactoryBean interface is a point that can be inserted into the instantiation logic of the Spring IoC container.
If you have complex initialization code, rather than (possibly) verbose XML, which can be better expressed in Java, you can create your own code FactoryBean,
Write a complex initialization in this class, and then insert the custom FactoryBean into the container.

The FactoryBean interface provides three methods:

  • Object getObject(): returns an instance of the object created by this factory. Instances can be shared, depending on whether the factory returns a singleton or prototype.
  • boolean isSingleton(): true. If FactoryBean returns a singleton or false, others will be returned.
  • Class getObjectType(): returns the object type returned by getObject() method, or null. If the type is unknown, the object type is returned.

This concept and interface are used in many places in the FactoryBeanSpring framework. Spring comes with more than 50 FactoryBean interface implementations. Little is known about in spring, but Mybatis's MybatisSqlSessionFactoryBean is very famous.

When you need to ask the container about the FactoryBean itself rather than the actual instance of the bean generated by it, please add the "&" symbol (&) before the id of the bean when calling the method.
Therefore, for a FactoryBean with a given id of myBean, calling getBean("myBean") returns the instance generated by the FactoryBean, and getBean ("& myBean") returns the FactoryBean itself.

public class MyFactoryBean implements FactoryBean<MyBean> {

    @Override
    public MyBean getObject() throws Exception {
        return new MyBean();
    }

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

<bean id="myFactoryBean" class="org.springframework.example.factoryBean.MyFactoryBean"/>

getBean("myFactoryBean")  Return is MyBean example
getBean("&myFactoryBean")  Return is MyFactoryBean example

Tags: Spring

Posted by danharibo on Fri, 13 May 2022 21:59:57 +0300