The fourth spring source code! Deep understanding of BeanDefinition

The fourth spring source code! Deep understanding of BeanDefinition

Gather the sand into a tower! Unknowingly, the Spring source code has been updated to the fifth one~

Before looking at the source code, you must first use the function. This is the most basic, because in the source code explanation, by default, everyone is already familiar with the basic usage of Spring. If you are not familiar with the basic usage of Spring, you can look at Songge s release on station B. Free introductory video: www.bilibili.com/video/BV1Wv ...

The last article and friends introduced the EntityResolver in the Spring source code, which is used to solve the XML file verification problem.

Next, the XML file parsing process of the second bullet should have been continued. Considering that we will involve an important concept BeanDefinition, some friends may not be familiar with it, so this article Song Ge Let s talk to you about what BeanDefinition is!

This article is the fifth article of Spring source code interpretation. Reading the previous articles in this series will help you better understand this article:

  1. Spring source code interpretation plan
  2. The first Spring source code is open! How is the configuration file loaded?
  3. The second spring source code! XML file parsing process
  4. The third spring source code! What the hell is EntityResolver?

1.BeanDefinition

In the Spring container, we widely use Beans one by one, and the BeanDefinition can be seen from the name of the Bean definition.

In fact, this is the case. The various properties of the Bean that we configure in the XML file are not only related to the object. The Spring container also needs to solve the Bean's life cycle, destruction, initialization and other operations. Bean's life cycle, destruction, initialization and other operations must have an object to carry, then this object is BeanDefinition.

Various attributes defined in XML will be loaded onto BeanDefinition first, and then a Bean will be generated through BeanDefinition. From this perspective, the relationship between BeanDefinition and Bean is somewhat similar to the relationship between class and object.

To understand BeanDefinition, we start from the inheritance relationship of BeanDefinition.

BeanDefinition is an interface that inherits from the BeanMetadataElement and AttributeAccessor interfaces.

  • BeanMetadataElement: This interface has only one method getSource, which returns the source of the Bean.
  • AttributeAccessor: This interface mainly regulates the method of asking the metadata of any object.

Let's take a look at AttributeAccessor:

public interface AttributeAccessor {
	void setAttribute(String name, @Nullable Object value);
	@Nullable
	Object getAttribute(String name);
	@Nullable
	Object removeAttribute(String name);
	boolean hasAttribute(String name);
	String[] attributeNames();
}
 

The metadata access interface is defined here, and the specific implementation is AttributeAccessorSupport. These data are stored using LinkedHashMap.

These are the two interfaces inherited by BeanDefinition. Next we look at the BeanDefinition interface:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
	int ROLE_APPLICATION = 0;
	int ROLE_SUPPORT = 1;
	int ROLE_INFRASTRUCTURE = 2;
	void setParentName(@Nullable String parentName);
	@Nullable
	String getParentName();
	void setBeanClassName(@Nullable String beanClassName);
	@Nullable
	String getBeanClassName();
	void setScope(@Nullable String scope);
	@Nullable
	String getScope();
	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();
	void setDependsOn(@Nullable String... dependsOn);
	@Nullable
	String[] getDependsOn();
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();
	void setPrimary(boolean primary);
	boolean isPrimary();
	void setFactoryBeanName(@Nullable String factoryBeanName);
	@Nullable
	String getFactoryBeanName();
	void setFactoryMethodName(@Nullable String factoryMethodName);
	@Nullable
	String getFactoryMethodName();
	ConstructorArgumentValues getConstructorArgumentValues();
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}
	MutablePropertyValues getPropertyValues();
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}
	void setInitMethodName(@Nullable String initMethodName);
	@Nullable
	String getInitMethodName();
	void setDestroyMethodName(@Nullable String destroyMethodName);
	@Nullable
	String getDestroyMethodName();
	void setRole(int role);
	int getRole();
	void setDescription(@Nullable String description);
	@Nullable
	String getDescription();
	ResolvableType getResolvableType();
	boolean isSingleton();
	boolean isPrototype();
	boolean isAbstract();
	@Nullable
	String getResourceDescription();
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}
 

Although there are many methods in BeanDefinition, combined with our usual configuration in XML, these methods are actually well understood:

  1. 1. two variables are defined at the beginning to describe whether the Bean is a singleton, and the later setScope/getScope methods can be used to modify/get the scope attribute.
  2. ROLE_xxx is used to describe the role of a bean. ROLE_APPLICATION indicates that the bean is a user-defined bean; ROLE_SUPPORT indicates that the bean is the supporting part of some complex configuration; ROLE_INFRASTRUCTURE indicates that this is a Spring internal bean, which can be modified through setRole/getRole.
  3. setParentName/getParentName to specify the name of the parent, this may have little use fewer partners, this corresponds to the XML <bean parent="">configuration.
  4. setBeanClassName/getBeanClassName this is to configure the Bean Class full path, corresponding to the XML <bean class="">configuration.
  5. setLazyInit/isLazyInit Configuration/Bean Gets whether lazy loading, this corresponds to the XML <bean lazy-init="">configuration.
  6. setDependsOn/getDependsOn configuration/Bean acquire the dependent objects, this corresponds to the XML <bean depends-on="">configuration.
  7. setAutowireCandidate/isAutowireCandidate configuration/Get Bean whether automatic assembly, corresponds to the XML <bean autowire-candidate="">configuration.
  8. setPrimary/isPrimary Configuration/Get the current choice of whether Bean Bean, corresponds to the XML <bean primary="">configuration.
  9. setFactoryBeanName/getFactoryBeanName Configuration/Get FactoryBean name corresponds to the XML <bean factory-bean="">configuration, factory-bean Song Ge talked about before entry video, small partners can be found here: www.bilibili.com/video/BV1Wv... .
  10. setFactoryMethodName/getFactoryMethodName and pairs of an upper, corresponds to the XML <bean factory-method="">configuration omitted.
  11. getConstructorArgumentValues returns the parameter values of the Bean construction method.
  12. hasConstructorArgumentValues Determines whether the previous item is an empty object.
  13. getPropertyValues This is to get a collection of common properties.
  14. hasPropertyValues Determines whether the previous item is an empty object.
  15. setInitMethodName/setDestroyMethodName configures the initialization method and destruction method of the Bean.
  16. setDescription/getDescription configures/returns the description of the bean.
  17. Whether isSingleton Bean is a singleton.
  18. isPrototype Bean is a prototype.
  19. isAbstract Whether the Bean is abstract.
  20. getResourceDescription returns the resource description that defines the Bean.
  21. getOriginatingBeanDefinition If the current BeanDefinition is a proxy object, then this method can be used to return the original BeanDefinition.

This is the definition of BeanDefinition and the meaning of its methods.

2. BeanDefinition implementation class

The above is just the definition of the BeanDefinition interface, BeanDefinition also has many implementation classes, let's get a general understanding.

First look at an inheritance diagram:

So many implementation classes look a bit dazzling, but after figuring out the role of each interface and class, it is easy to look at it again.

2.1 AbstractBeanDefinition

AbstractBeanDefinition is an abstract class, which provides corresponding attributes according to the interface defined in BeanDefinition and implements some of the methods defined in BeanDefinition. Originally, BeanDefinition only defined a series of get/set methods, and did not provide corresponding attributes. All attributes are defined in AbstractBeanDefinition.

The following other implementation classes are basically completed on the basis of AbstractBeanDefinition.

2.2 RootBeanDefinition

This is a more commonly used implementation class, corresponding to the general element tags.

2.3 ChildBeanDefinition

You can let the child BeanDefinition definition have the ability to inherit configuration from the parent BeanDefinition.

2.4 GenericBeanDefinition

GenericBeanDefinition is a newly added BeanDefinition implementation class since Spring 2.5. GenericBeanDefinition can dynamically set the parent Bean, and has the functions of RootBeanDefinition and ChildBeanDefinition at the same time.

2.5 AnnotatedBeanDefinition

Represents the annotation type BeanDefinition, which has the ability to obtain annotation metadata and method metadata.

2.6 AnnotatedGenericBeanDefinition

The @Configuration annotation mark configuration class will be parsed as AnnotatedGenericBeanDefinition.

3. Practice

Having said so much in theory, let's practice through a few lines of code to verify that what we said earlier is correct.

1. add spring-context dependency to the project, as follows:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
 

Then we create a User class, as follows:

public class User {
    private String username;
    private String address;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
 

Next we first verify the RootBeanDefinition. We manually define a RootBeanDefinition and register it in the Spring container.

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
ctx.refresh();
User bean = ctx.getBean(User.class);
System.out.println(bean);
 

MutablePropertyValues is a property in the definition object. When constructing the RootBeanDefinition, we pass in the class name and property set, and finally register the rootBeanDefinition in the container. The rest is done by the container, and then we can get the User object from the container.

The final output is as follows:

User{username='javaboy', address='www.javaboy.org'}
 

After reading this example, friends should be able to roughly understand that the various attributes we define in XML are first parsed into BeanDefinition, then registered in the Spring container, and finally get the Bean we need.

ChildBeanDefinition has the ability to inherit data from the parent Bean, let's see how to use this.

First create a new Person class. The Person class adds a nickname attribute to the User class, so that Person can inherit the values of the User s username and address attributes:

public class Person {
    private String username;
    private String address;
    private String nickname;

    @Override
    public String toString() {
        return "Person{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
}
 

Next, customize the ChildBeanDefinition:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("user");
childBeanDefinition.setBeanClass(Person.class);
childBeanDefinition.getPropertyValues().add("nickname", " ");
ctx.registerBeanDefinition("person", childBeanDefinition);
ctx.refresh();
User user = ctx.getBean(User.class);
Person person = ctx.getBean(Person.class);
System.out.println("user = " + user);
System.out.println("person = " + person);
 

First define the RootBeanDefinition and register it in the Spring container, and then define the ChildBeanDefinition. The ChildBeanDefinition inherits the existing attribute values in the RootBeanDefinition.

Finally, we get User and Person from the Spring container, and the print results are as follows:

user = User{username='javaboy', address='www.javaboy.org'}
person = Person{username='javaboy', address='www.javaboy.org', nickname=' '}
 

As you can see, Person does inherit the attribute value of User.

Both RootBeanDefinition and ChildBeanDefinition can be replaced by GenericBeanDefinition with the same effect, as follows:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(User.class);
rootBeanDefinition.setPropertyValues(pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setParentName("user");
childBeanDefinition.setBeanClass(Person.class);
childBeanDefinition.getPropertyValues().add("nickname", " ");
ctx.registerBeanDefinition("person", childBeanDefinition);
ctx.refresh();
User user = ctx.getBean(User.class);
Person person = ctx.getBean(Person.class);
System.out.println("user = " + user);
System.out.println("person = " + person);
 

The results of the operation are as follows:

user = User{username='javaboy', address='www.javaboy.org'}
person = Person{username='javaboy', address='www.javaboy.org', nickname=' '}
 

It can be seen that the effect is consistent with the previous operation.

In the case of our previous article in this series (the first article of the Spring source code is opened! How is the configuration file loaded? ), GenericBeanDefinition is also used by default, as follows:

Now that Spring Boot is widely popular, Java configuration is used more and more. Configuration classes marked with @Configuration annotation will be parsed as AnnotatedGenericBeanDefinition; Beans marked with @Bean annotation will be parsed as ConfigurationClassBeanDefinition.

We create a new MyConfig configuration class, as follows:

@Configuration
public class MyConfig {
    @Bean
    User user() {
        return new User();
    }
}
 

View the obtained BeanDefinition results are as follows:

Beans marked with other annotations such as @Service, @Controller, @Repository, and @Component will be recognized as ScannedGenericBeanDefinition. I won t demonstrate this one by one, you can test it yourself.

4. Summary

Well, today I will mainly introduce the BeanDefinition in Spring to my friends. Through the above principle + case, I believe that the friends have already understood that the Bean that we configure through XML or Java annotations, the things we define will first be parsed into BeanDefinition, and then the Bean we need is generated through BeanDefinition.

In the next article, we will look at how this BeanDefinition is generated from an XML file.

Okay, if you think you have gained something, remember to click and encourage Brother Panasonic~