Spring BeanDefinition (1)

Introduction of topic

Why do you want to read Spring source code? Some people want to learn the advanced ideas in Spring, some people want to better understand the design pattern, of course, a large number of people want to meet the interview, Spring Bean life cycle, Spring AOP principle, Spring IoC principle. If you want to really understand Spring, you need to spend a lot of time. You need to settle down and start from the basics. Today we will take a look at the basics of Spring — BeanDefinition.

What is a BeanDefinition

There is a detailed explanation on the Spring website. Let’s translate it: The SpringIoc container manages a Bean or beans that are created (for example, defined in XML) through configuration metadata that we provide to the container. In the container, the definition of these beans is represented by a BeanDefinition object, which contains the following metadata:

  • Fully qualified class name, usually the actual implementation class of the Bean;
  • Bean behavior configuration elements that describe the Bean’s behavior in the container (scope, lifecycle callbacks, and so on);
  • References to other beans, also called collaborators or dependencies, that the Bean needs to perform its work;
  • Other configuration information, for example, limits the size of the pool or the number of connections used in the bean that manages the connection pool.

Spring’s official website provides a detailed explanation of BeanDefinition, but it is not easy to understand. In fact, BeanDefinition is relatively easy to explain: BeanDefinition describes a Bean or BeanDefinition is the definition of a Bean.

Create a Java Bean that looks something like this:We write a Java file, we compile it into a Class file, we run the program, the Class loader loads the Class file, puts it into the methods section of the JVM, and we can have fun with new objects.

Create a Spring Bean that looks something like this:When we write a Java file, we compile it into a Class file, run the program, and the Class loader loads the Class file into the JVM’s methods section. This step remains the same. Spring will parse our configuration class (configuration file). Assuming that only A is configured, Spring will put the beanDefinitions of A into A map, which will then be processed one by one by the BeanPostProcessor. Finally, beans that have gone through the full Spring life cycle are put into singleObjects.

A bird ‘s-eye view of the BeanDefinition class diagram

As you can see, Spring’s BeanDefinition class diagram is quite complex. When I first read the Spring source code, I thought BeanDefinition would be a very simple thing, but later I realized that it wasn’t.

I’ll take a look at each of the classes involved.

AttributeAccessor

AttributeAccessor is an interface:

/** * Interface defining a generic contract for attaching and accessing metadata * to/from arbitrary objects. * * @author Rob Harrop * @since 2.0 */ public interface AttributeAccessor {void setAttribute(String name, @Nullable Object value); Object getAttribute(String name); Object removeAttribute(String name); boolean hasAttribute(String name); String[] attributeNames(); }Copy the code

The interface defines common methods for storing or reading metadata. If it’s an interface, there must be an implementation class, so let’s put that aside.

BeanMetadataElement

The BeanMetadataElement is also an interface that defines only one method:

/** * Interface to be implemented by bean metadata elements * that carry a configuration source object. * * @author Juergen Hoeller * @since 2.0 */ public interface BeanMetadataElement {@nullable Object getSource(); }Copy the code

The interface provides a method to retrieve the Bean’s source object. The source object is the source file.

@Configuration
@ComponentScan
public class AppConfig {
}
Copy the code
@Service
public class BookService {
}
Copy the code
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); System.out.println(context.getBeanDefinition("bookService").getSource()); }}Copy the code
file [D:\cycleinject\target\classes\com\codebear\springcycle\BookService.class]
Copy the code

Well, now I get it.

AttributeAccessorSupport

The AttributeAccessorSupport class is an abstract class that implements the AttributeAccessor interface. The AttributeAccessor interface, remember, defines generic methods to store or read metadata as virtual methods. This virtual method is implemented by AttributeAccessorSupport, which defines a map container in which the metadata is stored.

Why do we have this map

When I read the Spring source code for the first time, I was a little surprised to see the map. Shouldn’t metadata be stored in fields like BeanDefinition’s beanClass, Scope, and lazyInit? Isn’t this map done multiple times?

Spring is for extension purposes, or else BeanDefinition has a new feature, so you have to add a new field, for one thing. Second, what if a programmer extends Spring and the fields defined in BeanDefinition are no longer sufficient for the extension?

Does Spring use the map itself? Yes, it does. Let’s see what data Spring puts in the map:

public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); BeanDefinition appConfig = context.getBeanDefinition("appConfig"); for (String item : appConfig.attributeNames()) { System.out.println(item + ":" + appConfig.getAttribute(item)); }}Copy the code
org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass:full
org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass:true
Copy the code

As you can see, Spring puts two items inside:

  • The first item holds whether the configuration class is a Full configuration class. I wrote about the Full configuration class briefly in a previous blog post: Things you might not know about Spring
  • The second item, as its name suggests, is related to AOP.

BeanDefinition

BeanDefinition is an interface that inherits AttributeAccessor and BeanMetadataElement, both of which were introduced above.

BeanDefinition defines a number of methods, such as setBeanClassName, getBeanClassName, setScope, getScope, setLazyInit, isLazyInit, and so on. These methods are pretty straightforward, I won’t explain it here.

BeanMetadataAttributeAccessor

BeanMetadataAttributeAccessor inherited AttributeAccessorSupport, to save or read metadata method to carry on the further encapsulation.

AbstractBeanDefinition

AbstractBeanDefinition is an abstract class, inherited BeanMetadataAttributeAccessor, BeanDefinition is realized.

BeanDefinition implements most of the virtual methods defined by BeanDefinition, along with many constants and default values.

AbstractBeanDefinition has three subclasses, so let’s look at those three subclasses.

ChildBeanDefinition

As of Spring2.5, ChildBeanDefinition is no longer used, replaced by GenericBeanDefinition.

GenericBeanDefinition

GenericBeanDefinition replaces ChildBeanDefinition, which literally means “ChildBeanDefinition”. Does BeanDefinition still have a father-son relationship? B: of course.

public class ChildService { private int id; private String name; public ChildService(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code
public class ParentService { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ParentService(int id, String name) { this.id = id; this.name = name; }}Copy the code
 public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        GenericBeanDefinition parentBeanDefinition = new GenericBeanDefinition();
        parentBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentBeanDefinition.setAttribute("name", "codebear");
        parentBeanDefinition.setAbstract(true);
        parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(1);
        parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue("CodeBear");

        GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
        childBeanDefinition.setParentName("parent");
        childBeanDefinition.setBeanClass(ChildService.class);

        context.registerBeanDefinition("parent", parentBeanDefinition);
        context.registerBeanDefinition("child", childBeanDefinition);

        context.refresh();

        BeanDefinition child = context.getBeanFactory().getMergedBeanDefinition("child");
        for (String s : child.attributeNames()) {
            System.out.println(s + ":" + child.getAttribute(s));
        }
        System.out.println("scope:" + child.getScope());

        System.out.println("-------------------");

        ChildService service = context.getBean(ChildService.class);
        System.out.println(service.getName());
        System.out.println(service.getId());
    }
Copy the code

Running results:


name:codebear
scope:singleton
-------------------
CodeBear
1
Copy the code

Let’s look at the code:

  1. Create the GenericBeanDefinition object parentBeanDefinition, set it to singleton mode, set the Attribute, declare the constructor’s two parameter values;
  2. Create the GenericBeanDefinition object childBeanDefinition and set parentName to parent and BeanClass to ChildService.
  3. Register parentBeanDefinition, beanName as parent, childBeanDefinition, beanName as child;
  4. Refresh the container;
  5. MergedBeanDefinitions takes the Child from mergedBeanDefinitions, which houses the combined BeanDefinition.
  6. Print the attribute, scope, and constructor values for the child.

As you can see, childBeanDefinition inherits parentBeanDefinition.

If there is no parent-child relationship, it can also be used as a BeanDefinition alone, or GenericBeanDefinition:

       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(AuthorService.class);
        genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        context.registerBeanDefinition("authorService", genericBeanDefinition);

        context.refresh();

        BeanDefinition mergedBeanDefinition = context.getBeanFactory().getMergedBeanDefinition("authorService");
        BeanDefinition beanDefinition = context.getBeanFactory().getMergedBeanDefinition("authorService");

        System.out.println(mergedBeanDefinition);
        System.out.println(beanDefinition);
Copy the code

Running results:

Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Copy the code

As you can see, when there is no parent-child relationship, a beanDefinition is still stored in mergedBeanDefinitions, but the contents are the same as those stored in beanDefinitions.

GenericBeanDefinition summary

GenericBeanDefinition replaces the lower version of Spring’s ChildBeanDefinition. GenericBeanDefinition is more flexible than ChildBeanDefinition and RootBeanDefinition. It can be a BeanDefinition alone, a parent BeanDefinition, or a child GenericBeanDefinition.

RootBeanDefinition

When I introduced GenericBeanDefinition, I wrote two pieces of code.

Put a breakpoint on the first code and look at mergedBeanDefinitions, Both parentBeanDefinition and childBeanDefinition become RootBeanDefinition in mergedBeanDefinitions:

Put a breakpoint on the second code and also look at mergedBeanDefinitions. You can see that authorService also changes to RootBeanDefinition under mergedBeanDefinitions:

As you can see, mergedBeanDefinitions are all root beandefinitions.

RootBeanDefinition can also be used to act as a parent BeanDefinition, as in the following:

 public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
        genericBeanDefinition.setBeanClass(ParentService.class);
        genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        context.registerBeanDefinition("parent", genericBeanDefinition);

        GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
        rootBeanDefinition.setBeanClass(ChildService.class);
        rootBeanDefinition.setParentName("parent");

        context.refresh();
    }
Copy the code

But a RootBeanDefinition cannot act as a child BeanDefinition:

  public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
        genericBeanDefinition.setBeanClass(ParentService.class);
        genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        context.registerBeanDefinition("parent", genericBeanDefinition);

        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(ChildService.class);
        rootBeanDefinition.setParentName("parent");

        context.refresh();
    }
Copy the code

Running results:

Exception in thread "main" java.lang.IllegalArgumentException: Root bean cannot be changed into a child bean with parent reference
	at org.springframework.beans.factory.support.RootBeanDefinition.setParentName(RootBeanDefinition.java:260)
	at com.codebear.springcycle.Main.main(Main.java:20)
Copy the code

An exception was thrown.

Query source code:

	@Override
	public void setParentName(@Nullable String parentName) {
		if (parentName != null) {
			throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
		}
	}
Copy the code

Found that calling the setParentName method of RootBeanDefinition directly raised an exception.

RootBeanDefinition summary

RootBeanDefinition can act as a parent BeanDefinition of another BeanDefinition, or as a BeanDefinition alone, but not as a child BeanDefinition of another BeanDefinition, MergedBeanDefinitions are all rootBeanDefinitions stored at mergedBeanDefinitions.

ScannedGenericBeanDefinition

@Configuration
@ComponentScan
public class AppConfig {
}
Copy the code
@Service
public class AuthorService {
}
Copy the code
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); System.out.println(context.getBeanDefinition("authorService").getClass()); }}Copy the code

Running results:

class org.springframework.context.annotation.ScannedGenericBeanDefinition
Copy the code

Through annotation scanning Bean ScannedGenericBeanDefinition BeanDefinition used to say.

AnnotatedGenericBeanDefinition

 public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(context.getBeanDefinition("appConfig").getClass());
    }
Copy the code

Running results:

class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
Copy the code

The configuration class AnnotatedGenericBeanDefinition BeanDefinition used to say.

ConfigurationClassBeanDefinition

public class AuthorService {
}
Copy the code
@Configuration @ComponentScan public class AppConfig { @Bean public AuthorService authorService() { return null; }}Copy the code
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
 System.out.println(context.getBeanDefinition("authorService").getClass());
    }
Copy the code

Running results:

  class org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition
Copy the code

Use @ the Bean Bean declaration BeanDefinition use ConfigurationClassBeanDefinition.

Is it completely unexpected that a BeanDefinition can involve so many things, things that are useless, things that are useless; It works, it works. It’s confusing to read the Spring source code, but why are there so many BeanDefinitions? At this point, you’ll get stuck trying to figure out what these Beandefinitions are for, but there aren’t too many blogs on the web, and even fewer good ones. Hopefully this article will fill in the blanks.