Brief Analysis of Spring Loading Bean Process

Keywords: Spring xml Java Attribute

ConfigurationClassBeanDefinitionReader

1 How bean s are defined

Common ways to define beans are:

  • Through xml, for example:
    <bean id="dictionaryRelMap" class="java.util.HashMap"/>
  • Use @Component and other annotations on Class through annotations, such as
    @Component
    public class xxxServicer{
    ....
    }
  • By @Bean under the @Configuration class, for example
    @Configuration
    public class xxxConfiguration{
    @Bean
    public myBean myBean(){
        return new myBean();
    }
    }

Although the three ways of defining beans are different and the corresponding details are different, they are the same logically. The main process is as follows: The key problem is to find the way to define beans, then generate Bean Definition, register it in Spring context, and automatically create instances of beans by Spring.

2 BeanDefinition

BeanDefinition is an interface that describes a bean instance, such as SINGLETON or PROTOTYPE, what is the value of an attribute, what are the parameters of a constructor, and so on. Simply put, through a Bean Definition, we can complete a Bean instantiation. BeanDefinition and its main subclasses:


Here's a brief description of each subclass:

  • RootBean Definition and ChildBean Definition: These two Bean Definitions are relative and have been replaced by GenericBean Definition since Spring 2.5 came out. Because it forces us to know the relationship between them when we write code.
  • GenericBean Definition: Compared with RootBean Definition and ChildBean Definition, which must be hard-coded when defined, GenericBean Definition has the advantage of dynamically setting parent for GenericBean Definition.
  • Annotated Bean Definition: Look at the name to know that it is used to read beans defined through annotations.

3 Define Bean s through xml files

Defining beans through xml was the earliest Spring way to define beans. Therefore, how to parse the xml tag into Bean Definition (), The entry is in the class of org.springframework.beans.factory.xml.XmlBeanDefinitionReader, but the actual work is in the class of org.springframework.beans.factory.xml.BeanDefinitionParserDelegate. There's a lot of code, but the actual logic is simple: parse the labels defined by Spring, such as < bean > < property >. Previously, I wrote an article about how to customize Spring tags, parse them and register them in Spring.—— Portal

4 Load Bean s with Spring-supported annotations such as @Component

If you want to use annotations such as @Component to define beans, a prerequisite is that there are & lt; context: component-scan/> or @ComponentScan annotations. But there is still a difference between the two ways:

4.1 <context:component-scan/>

Because & lt; context: component-scan/> is an xml tag, it is parsing xml and generating the class org. spring framework. context. annotation. ComponentScanBean Definition Parser, key code:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
        //Get the base-package tag
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // The actual processing class is ClassPathBeanDefinitionScanner 
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //Scan all the classes under the base Package and register with Spring if there are @Component tags, etc.
    Set&lt;BeanDefinitionHolder&gt; beanDefinitions = scanner.doScan(basePackages);
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

4.2 @ComponentScan

Annotations correspond to generated classes that are org. spring framework. context. annotation. ComponentScanAnnotation Parser In fact, the last thing actually worked was the ClassPathBean Definition Scanner. The ComponentScan AnnotationParser class is generated with the @Configuration annotation process (meaning @ComponentScan must be used with @Configuration). Processing @Configuration is actually org.spring framework.context.annotation.Configuration ClassPostProcessor. Does it feel a little twisted?
Simply put, when you find an @ComponentScan annotation when you process @Configuration, you generate a ComponentScan Annotation Parser to scan the @Component annotation.

4.3 ClassPathBeanDefinitionScanner

As mentioned above, both annotations and labels will eventually be handled by the ClassPathBean Definition Scanner class, which does 1. Scan all classes under the base Package, if there are @Component and other annotations, read the @Component related properties, generate Scanned GenericBean Definition, and register it in Spring.

5 By @Bean

As mentioned earlier, @ComponentScan is a part of the @Configuration process. Since the @Bean annotation must also be used with @Configuration, the processing of @Bean is also in @Configuration. In fact, it is handled by the class Configuration ClassBean Definition Reader. Key code:

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
        TrackedConditionEvaluator trackedConditionEvaluator) {

       //If you define yourself through the @Import annotation, you need to register yourself in Spring
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //Here is the @Bean on the processing method.
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //Processing @ImportResource, where parsing xml is the XmlBean Definition Reader for parsing xml mentioned above
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

To ensure org. spring framework. context. annotation. Configuration ClassPostProcessor

6 instantiate BeanDefinition

I've talked about how to convert different ways of defining beans into Bean Definition and add it to Spring (which is exactly kept in BeanFactory's Bean Definition Map). The example is in the final stage of Application Context, and the key code is in DefaultListable BeanFactory.

    @Override
    public void preInstantiateSingletons() throws BeansException {

        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() &amp;&amp; bd.isSingleton() &amp;&amp; !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean&lt;?&gt; factory = (FactoryBean&lt;?&gt;) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null &amp;&amp; factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction&lt;Boolean&gt;() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean&lt;?&gt;) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &amp;&amp;
                                ((SmartFactoryBean&lt;?&gt;) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }

Through the code of the last instance of getBean, in AbstractAutowire Capable BeanFactory

    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        //Processing xxAware interface
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction&lt;Object&gt;() {
                @Override
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

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

        try {
           //Initialization, first determine whether it is Initializing Bean,
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }

        if (mbd == null || !mbd.isSynthetic()) {
               // Call BeanPostProcessors#postProcessAfterInitialization
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

From the above initialization, you can see the invocation order of InitializeBean and BeanPostProcessors

7 Summary

In summary, Spring loads beans with the same idea. First, it reads relevant information to generate Bean Definition, and then initializes beans through Bean Definition. If you know the above routines, you can clearly customize Xml tags or custom annotations to inject beans into Spring.

Posted by mikesch on Sun, 07 Jul 2019 20:02:28 -0700