Spring - what is BeanDefinition?

Posted by f1r3fl3x on Sat, 25 Dec 2021 19:17:31 +0100

text

What is BeanDefinition?

BeanDefinition, which is too important, is the core storage structure.

What does spring do? After spring came out, it mainly focuses on IOC containers. What are the containers for? Beans? What are beans? I don't know. Anyway, beans are taken out of spring.

Since you don't know, you always know what characteristics he has,

1. Just like you fried chicken fillet. I don't know what chicken fillet is. It is characterized by yellow red with sauce. Generally, it is fried, crisp and tender

2. In other words, in general, employees should fill in kqi every quarter. Name, Department, project, position, etc.

What about Bean

There are abstract (whether it is abstract) and lazy init (whether to delay initialization). If true, it means to get and create the object. The default is false. spring officially supports this. Problems are exposed in advance, beanClass, beanclassiname (bean class name) and parent (parent class)

, constructoragementvlues, PropertiesValuesList, Scope singleton, prototype, poor reflection, there are factory bean s, factory methods, role s (framework or application), and quite a few...

Since these features and things of each Bean are in a template, and the basic format is fixed, we can abstract them and make a format.

@Data
public class SpringBean {

    /**
     * bean class name
     */
    String beanClassName;

    /**
     * The name of the factory bean
     */
    String factoryBeanName;

    /**
     * Name of factory method
     */
    String factoryMethodName;

    /**
     * singleton,prototype
     */
    String scope;

    /**
     * Delay initialization
     */
    boolean isLazyInit;

    /**
     * Dependent bean
     */
    String[] dependsOn;

    /**
     * bean Role, 1framework, 2application
     */
    int role;

    /**
     * Is it a primary candidate bean
     */
    boolean primary;

    /**
     * Is it abstract
     */
    boolean isAbstract;
    
    .......
}

In other words, you can create a lightweight container according to such a template. Is it true that some lightweight ioc containers play like this? spring is certainly not so low. The framework has the title of the king of extensibility, which is convenient for us to replace and implement,

Here, the interface should be used to abstract. It must be abstracted as an interface.

Let's take a look at spring's description of its interface

A BeanDefinition describes a bean instance, which has property values, constructor argument values, and further information supplied by concrete implementations.
This is just a minimal interface: The main intention is to allow a BeanFactoryPostProcessor to introspect and modify property values and other bean metadata.
Since:
19.03.2004
See Also:
ConfigurableListableBeanFactory.getBeanDefinition, org.springframework.beans.factory.support.RootBeanDefinition, org.springframework.beans.factory.support.ChildBeanDefinition
Author:
Juergen Hoeller, Rob Harrop

 

That is to say, BeanDefinition , describes the attribute values and construction method values of a Bean instance, and specifically declares that this is a minimized interface, and the main purpose is to allow beanfactoryprocessor post processor

To modify property values and metadata of other bean s. The interface in 2004 can be imagined as how core the interface is.

Let's look at the specific definition methods to better understand:

/**
     * Set the name of the parent definition of this bean definition, if any.
     */
    void setParentName(@Nullable String parentName);

    /**
     * Return the name of the parent definition of this bean definition, if any.
     */
    @Nullable
    String getParentName();
/**
     * Specify the bean class name of this bean definition.
     * <p>The class name can be modified during bean factory post-processing,
     * typically replacing the original class name with a parsed variant of it.
     * @see #setParentName
     * @see #setFactoryBeanName
     * @see #setFactoryMethodName
     */
    void setBeanClassName(@Nullable String beanClassName);

    /**
     * Return the current bean class name of this bean definition.
     * <p>Note that this does not have to be the actual class name used at runtime, in
     * case of a child definition overriding/inheriting the class name from its parent.
     * Also, this may just be the class that a factory method is called on, or it may
     * even be empty in case of a factory bean reference that a method is called on.
     * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
     * rather only use it for parsing purposes at the individual bean definition level.
     * @see #getParentName()
     * @see #getFactoryBeanName()
     * @see #getFactoryMethodName()
     */
    @Nullable
    String getBeanClassName();
/**
     * Override the target scope of this bean, specifying a new scope name.
     * @see #SCOPE_SINGLETON
     * @see #SCOPE_PROTOTYPE
     */
    void setScope(@Nullable String scope);

    /**
     * Return the name of the current target scope for this bean,
     * or {@code null} if not known yet.
     */
    @Nullable
    String getScope();
/**
     * Set whether this bean should be lazily initialized.
     * <p>If {@code false}, the bean will get instantiated on startup by bean
     * factories that perform eager initialization of singletons.
     */
    void setLazyInit(boolean lazyInit);

    /**
     * Return whether this bean should be lazily initialized, i.e. not
     * eagerly instantiated on startup. Only applicable to a singleton bean.
     */
    boolean isLazyInit();
/**
     * Set the names of the beans that this bean depends on being initialized.
     * The bean factory will guarantee that these beans get initialized first.
     */
    void setDependsOn(@Nullable String... dependsOn);

    /**
     * Return the bean names that this bean depends on.
     */
    @Nullable
    String[] getDependsOn();
/**
     * Set whether this bean is a candidate for getting autowired into some other bean.
     * <p>Note that this flag is designed to only affect type-based autowiring.
     * It does not affect explicit references by name, which will get resolved even
     * if the specified bean is not marked as an autowire candidate. As a consequence,
     * autowiring by name will nevertheless inject a bean if the name matches.
     */
    void setAutowireCandidate(boolean autowireCandidate);

    /**
     * Return whether this bean is a candidate for getting autowired into some other bean.
     */
    boolean isAutowireCandidate();
/**
     * Set whether this bean is a primary autowire candidate.
     * <p>If this value is {@code true} for exactly one bean among multiple
     * matching candidates, it will serve as a tie-breaker.
     */
    void setPrimary(boolean primary);

    /**
     * Return whether this bean is a primary autowire candidate.
     */
    boolean isPrimary();
/**
     * Specify the factory bean to use, if any.
     * This the name of the bean to call the specified factory method on.
     * @see #setFactoryMethodName
     */
    void setFactoryBeanName(@Nullable String factoryBeanName);

    /**
     * Return the factory bean name, if any.
     */
    @Nullable
    String getFactoryBeanName();
/**
     * Specify a factory method, if any. This method will be invoked with
     * constructor arguments, or with no arguments if none are specified.
     * The method will be invoked on the specified factory bean, if any,
     * or otherwise as a static method on the local bean class.
     * @see #setFactoryBeanName
     * @see #setBeanClassName
     */
    void setFactoryMethodName(@Nullable String factoryMethodName);

    /**
     * Return a factory method, if any.
     */
    @Nullable
    String getFactoryMethodName();
**
     * Return the constructor argument values for this bean.
     * <p>The returned instance can be modified during bean factory post-processing.
     * @return the ConstructorArgumentValues object (never {@code null})
     */
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * Return if there are constructor argument values defined for this bean.
     * @since 5.0.2
     */
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    /**
     * Return the property values to be applied to a new instance of the bean.
     * <p>The returned instance can be modified during bean factory post-processing.
     * @return the MutablePropertyValues object (never {@code null})
     */
    MutablePropertyValues getPropertyValues();

 

If you take a closer look, you can find that it is similar to the get and set of the class we usually define.

After reading, look , look, what are the implementation interfaces of BeanDefinition?

 

 

The sub interface AnnotatedBeanDefinition that can obtain annotation information

/**
 * Extended {@link org.springframework.beans.factory.config.BeanDefinition}
 * interface that exposes {@link org.springframework.core.type.AnnotationMetadata}
 * about its bean class - without requiring the class to be loaded yet.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see AnnotatedGenericBeanDefinition
 * @see org.springframework.core.type.AnnotationMetadata
 */
public interface AnnotatedBeanDefinition extends BeanDefinition {

    /**
     * Obtain the annotation metadata (as well as basic class metadata)
     * for this bean definition's bean class.
     * @return the annotation metadata object (never {@code null})
     */
    AnnotationMetadata getMetadata();

    /**
     * Obtain metadata for this bean definition's factory method, if any.
     * @return the factory method metadata, or {@code null} if none
     * @since 4.1.1
     */
    @Nullable
    MethodMetadata getFactoryMethodMetadata();

}

 

This annotation can obtain the annotation metadata marked on the corresponding bean class

Like controller

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    @AliasFor(annotation = Component.class)
    String value() default "";

}

 

The value value of the controller annotation can be obtained through the AnnotatedBeanDefinition

I wrote a simple demo here

@Controller("xxxxxxxxxxxxxxxxx")
@Service("yyyyyyyyyyyyyyy")
public class TestController {

    @Autowired
    TestService testService;

    @Override
    public String toString() {
        return "TestController{" +
                "testService=" + testService +
                '}';
    }
}
@Slf4j
public class RunAnnotatedBeanDefinitionTest {

    public static void main(String[] args) {
        AnnotationConfigReactiveWebApplicationContext beanFactory = new AnnotationConfigReactiveWebApplicationContext();
        beanFactory.refresh();
        beanFactory.registerBean("yyy",TestController.class,null);

        AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) beanFactory.getBeanDefinition("yyy");
        AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
        Map<String,Object> resultC = annotationMetadata.getAnnotationAttributes("org.springframework.stereotype.Controller");
        Map<String,Object> resultS = annotationMetadata.getAnnotationAttributes("org.springframework.stereotype.Service");

        log.info("{}",resultC);
        log.info("{}",resultS);
    }
}

 

I printed it here:

16:09:02.923 [main] INFO com.spring.learn.RunAnnotatedBeanDefinitionTest - {value=xxxxxxxxxxxxxxxxx}
16:09:02.926 [main] INFO com.spring.learn.RunAnnotatedBeanDefinitionTest - {value=yyyyyyyyyyyyyyy}

 

Implementation classes under AnnotatedBeanDefinition and BeanDefinition interfaces

The implementation classes are similar. This is their UML diagram

 

 

Basically, AbstractBeanDefinition acts as a concrete implementation. Almost all of them have been implemented, and only one method has not been implemented

/**
     * Clone this bean definition.
     * To be implemented by concrete subclasses.
     * @return the cloned bean definition object
     */
    public abstract AbstractBeanDefinition cloneBeanDefinition();

 

The beanDefinition of clone, which is not implemented, is to leave subclass extensions. You can put hashmap, currentHashMap, redis, and so on.

Look again, org springframework. beans. factory. support. Genericbeandefinition is very important

public class GenericBeanDefinition extends AbstractBeanDefinition {

    @Nullable
    private String parentName;


    /**
* I haven't noticed yet. This is a bit similar to the builder mode. Create an instance and set various attribute values
* Create a new GenericBeanDefinition, to be configured through its bean * properties and configuration methods. * @see #setBeanClass * @see #setScope * @see #setConstructorArgumentValues * @see #setPropertyValues */ public GenericBeanDefinition() { super(); } /** * Create a new GenericBeanDefinition as deep copy of the given * bean definition. * @param original the original bean definition to copy from */ public GenericBeanDefinition(BeanDefinition original) { super(original); } @Override public void setParentName(@Nullable String parentName) { this.parentName = parentName; } @Override @Nullable public String getParentName() { return this.parentName; } @Override public AbstractBeanDefinition cloneBeanDefinition() { return new GenericBeanDefinition(this); } }

 

Look at the two spring beans packages, org springframework. beans. factory. annotation. AnnotatedGenericBeanDefinition

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

    private final AnnotationMetadata metadata;

    @Nullable
    private MethodMetadata factoryMethodMetadata;


    /**
     * Create a new AnnotatedGenericBeanDefinition for the given bean class.
     * @param beanClass the loaded bean class
     */
    public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
        setBeanClass(beanClass);
        this.metadata = AnnotationMetadata.introspect(beanClass);
    }

    /**
     * Create a new AnnotatedGenericBeanDefinition for the given annotation metadata,
     * allowing for ASM-based processing and avoidance of early loading of the bean class.
     * Note that this constructor is functionally equivalent to
     * {@link org.springframework.context.annotation.ScannedGenericBeanDefinition
     * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that a
     * bean was discovered specifically via component-scanning as opposed to other means.
     * @param metadata the annotation metadata for the bean class in question
     * @since 3.1.1
     */
    public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {
        Assert.notNull(metadata, "AnnotationMetadata must not be null");
        if (metadata instanceof StandardAnnotationMetadata) {
            setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
        }
        else {
            setBeanClassName(metadata.getClassName());
        }
        this.metadata = metadata;
    }

    /**
     * Create a new AnnotatedGenericBeanDefinition for the given annotation metadata,
     * based on an annotated class and a factory method on that class.
     * @param metadata the annotation metadata for the bean class in question
     * @param factoryMethodMetadata metadata for the selected factory method
     * @since 4.1.1
     */
    public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata, MethodMetadata factoryMethodMetadata) {
        this(metadata);
        Assert.notNull(factoryMethodMetadata, "MethodMetadata must not be null");
        setFactoryMethodName(factoryMethodMetadata.getMethodName());
        this.factoryMethodMetadata = factoryMethodMetadata;
    }


    @Override
    public final AnnotationMetadata getMetadata() {
        return this.metadata;
    }

    @Override
    @Nullable
    public final MethodMetadata getFactoryMethodMetadata() {
        return this.factoryMethodMetadata;
    }

}

 

That is, there is more annotation function to obtain bean class

Check out org springframework. context. annotation. ScannedGenericBeanDefinition

/**
 * Extension of the {@link org.springframework.beans.factory.support.GenericBeanDefinition}
 * class, based on an ASM ClassReader, with support for annotation metadata exposed
 * through the {@link AnnotatedBeanDefinition} interface.
 *
 * <p>This class does <i>not</i> load the bean {@code Class} early.
 * It rather retrieves all relevant metadata from the ".class" file itself,
 * parsed with the ASM ClassReader. It is functionally equivalent to
 * {@link AnnotatedGenericBeanDefinition#AnnotatedGenericBeanDefinition(AnnotationMetadata)}
 * but distinguishes by type beans that have been <em>scanned</em> vs those that have
 * been otherwise registered or detected by other means.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.5
 * @see #getMetadata()
 * @see #getBeanClassName()
 * @see org.springframework.core.type.classreading.MetadataReaderFactory
 * @see AnnotatedGenericBeanDefinition
 */
@SuppressWarnings("serial")
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

    private final AnnotationMetadata metadata;


    /**
     * Create a new ScannedGenericBeanDefinition for the class that the
     * given MetadataReader describes.
     * @param metadataReader the MetadataReader for the scanned target class
     */
    public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
        Assert.notNull(metadataReader, "MetadataReader must not be null");
        this.metadata = metadataReader.getAnnotationMetadata();
        setBeanClassName(this.metadata.getClassName());
        setResource(metadataReader.getResource());
    }


    @Override
    public final AnnotationMetadata getMetadata() {
        return this.metadata;
    }

    @Override
    @Nullable
    public MethodMetadata getFactoryMethodMetadata() {
        return null;
    }

}

 

At first glance, the annotation uses asm to obtain annotation information. The difference is

org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition, under the beans package, use reflection

org.springframework.context.annotation.ScannedGenericBeanDefinition, under the context package, use asm

Take another look at http} org springframework. beans. factory. support. Rootbeandefinition (under the spring beans package). This class has subclasses under the context package (org.springframework.context.annotation)

org. springframework. context. annotation. ConfigurationClassBeanDefinitionReader. The configurationclassbeandefinition should be closely related to the @ Configuration annotation.

 

Do an exercise to use the implementation class of AnnotatedBeanDefinition.

 

Topics: Spring