Spring source code continue to open full!

In this article, Songo shares with you how to load configuration files in Spring. If you haven’t seen it, you must take a look at it first. This will help you better understand this article. How is the configuration file loaded? .

Remember the code from that article?

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = factory.getBean(User.class);
System.out.println("user = " + user);
Copy the code

Once the ClassPathResource outputs the file as an IO stream, the next step is to construct an XmlBeanFactory, which has limited capabilities, Most of its functions are realized in its superclass DefaultListableBeanFactory, and DefaultListableBeanFactory also quite so the father of the container, why do you say that? Let’s talk about that today.

This is the seventh Spring source read-through, and reading the previous articles in this series will help you understand it better:

  1. Spring source code interpretation plan
  2. Spring source code first open whole! How is the configuration file loaded?
  3. Spring source code second bomb! XML file parsing process
  4. Spring source code third bullet! What the hell is EntityResolver?
  5. Spring source code fourth bomb! Understand the BeanDefinition in depth
  6. Build Spring source code analysis environment for you

1.DefaultListableBeanFactory

Say XmlBeanFactory had to say it is the parent of the first DefaultListableBeanFactory, Because most of the XmlBeanFactory function in DefaultListableBeanFactory has actually provide good, IO stream of XmlBeanFactory read made some custom.

DefaultListableBeanFactory is a complete, functional mature the IoC container, if the demand is very simple, you can even use DefaultListableBeanFactory directly, if your demand is more complex, By extending DefaultListableBeanFactory function also can achieve, then, to say the DefaultListableBeanFactory is the ancestor of the Spring IoC container.

Let’s look at some of the DefaultListableBeanFactory inheritance relationships:

Can be seen from this class diagram, DefaultListableBeanFactory actually is also a master. In Spring, according to different operation of Bean has a different interface specification, each interface has its own corresponding implementation, eventually will bring together all of the implementation in DefaultListableBeanFactory. This class inheritance diagram gives you a sense of how Spring’s class design is very poorly coupled.

Most of these classes will be covered later in this series, but let me give you an overview of what each class does.

  1. BeanFactory: This interface is a Bean factory by name. The BeanFactory interface defines various basic bean-specific methods for obtaining beans, determining whether beans exist, determining whether beans are singletons, and so on.
  2. ListableBeanFactory: This interface inherits from BeanFactory and extends Bean query methods, such as getting BeanNames by type, BeanNames by annotation, and annotations by Bean.
  3. AutowireCapableBeanFactory: this interface inherits from the BeanFactory, on the basis of the BeanFactory, provides the beans to create, configure, injection, destroy, etc. Sometimes when we need to inject beans manually ourselves, we can consider implementing this interface to do so. AutowireCapableBeanFactory has an important application in Spring Security is ObjectPostProcessor, the scene will be in the Spring Security series and all details.
  4. HierarchicalBeanFactory: This interface inherits from BeanFactory and adds methods to get the Parent BeanFactory.
  5. SingletonBeanRegistry: This interface defines the definition of singleton beans and how to get them.
  6. ConfigurableBeanFactory: This interface specifies the various configurations and destruction methods for the BeanFactory.
  7. ConfigurableListableBeanFactory: this is the BeanFactory configuration list, defines the types of neglect, interface here, by the name of the Bean to obtain BeanDefinition BeanDefinition or freeze.
  8. AliasRegistry: This interface defines the registration, removal, determination, and query operations for aliases.
  9. SimpleAliasRegistry: This class implements the AliasRegistry interface and its methods. SimpleAliasRegistry uses ConcurrentHashMap to register, remove, and query aliases.
  10. DefaultSingletonBeanRegistry: this class is based on the collection of Java, on SingletonBeanRegistry interface implementation.
  11. FactoryBeanRegistrySupport: The class inherits from DefaultSingletonBeanRegistry, and on the basis of DefaultSingletonBeanRegistry, increased access to FactoryBean type, remove FactoryBean cache method, etc.
  12. AbstractBeanFactory: Implements the ConfigurableBeanFactory interface and inherited from FactoryBeanRegistrySupport, Methods defined in ConfigurableBeanFactory are implemented in AbstractBeanFactory.
  13. AbstractAutowireCapableBeanFactory: this class inherits from AbstractBeanFactory and AutowireCapableBeanFactory interface methods defined in the fall to the ground.
  14. BeanDefinitionRegistry: This interface inherits from the AliasRegistry interface and adds a set of methods for registering, removing, querying, determining, and so on for BeanDefinitions.
  15. The final DefaultListableBeanFactory naturally possess the function of all the above.

You can see the content of the above dazzling, elder brother here with a few simple practical examples, to take you to use the DefaultListableBeanFactory function, may everyone’s understanding is clear.

DefaultListableBeanFactory as a master, provides a lot of function, we see one by one.

2. Code transformation

First article one of the three lines of code we can begin the slightly modified, because we have already said most of the XmlBeanFactory function actually in DefaultListableBeanFactory has provided good, XmlBeanFactory simply customizations IO stream readings. File readings are mostly XmlBeanDefinitionReader (described in previous articles in this series). We can modify the first three lines of this article. In order to better reflect the “most of the XmlBeanFactory function in DefaultListableBeanFactory actually already provide good” :

ClassPathResource res=new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
User user = factory.getBean(User.class);
System.out.println("user = " + user);
Copy the code

Replace the XmlBeanFactory with the first four lines of code. Is it clear what the XmlBeanFactory does? That’s what the first four lines of code do.

3. Dynamically register beans

Dynamic registration Bean, this is one of the functions of DefaultListableBeanFactory, but it should be dynamically register BeanDefinition accurately.

Let’s start with a simple example:

DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username"."javaboy");
pvs.add("address"."www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
Copy the code

First of all, our own manual build a DefaultListableBeanFactory object. You can also use the XmlBeanFactory above.

Then manually build a GenericBeanDefinition. In the previous article, Songo explained that the default BeanDefinition we use now is a GenericBeanDefinition, so let’s build a GenericBeanDefinition ourselves manually. Now that we have GenericBeanDefinition, we set the associated classes and properties.

Next to userBeanDefinition registered defaultListableBeanFactory again. Registration is completed, we can obtain corresponding beans from defaultListableBeanFactory.

As an aside, I hope that when you read each article in this series, you will be able to relate the articles before and after this series together to understand, so that there will be a lot of unexpected harvest. Such as the above, we can declare a DefaultListableBeanFactory, can also declare a XmlBeanFactory, then you probably can therefore conclude that the main purpose of the XmlBeanFactory may be read on resource files and registration.

So how do you register? Let’s take a look at defaultListableBeanFactory. RegisterBeanDefinition method definition:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {
	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");
	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if(existingDefinition ! =null) {
		if(! isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]"); }}else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]"); }}this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			synchronized (this.beanDefinitionMap) {
				this.beanDefinitionMap.put(beanName, beanDefinition);
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); }}else {
			// Still in startup registration phase
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	if(existingDefinition ! =null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
	else if(isConfigurationFrozen()) { clearByTypeCache(); }}Copy the code

RegisterBeanDefinition method is in BeanDefinitionRegistry interface declaration, DefaultListableBeanFactory class implements the BeanDefinitionRegistry interface, And implemented the method, let’s look at the analysis of the method:

  1. Firstly, the beanDefinition object passed in is verified, which is also the last verification before registration. However, at this time, the beanDefinition object is already in hand, so the verification is not XML file verification, but mainly verification of methodOverrides.
  2. Next, you get beanDefinitionMap from beanDefinitionMap based on beanName to see if the current Bean has been defined. BeanDefinitionMap is a set of maps where the key is beanName and the value is a BeanDefinition object.
  3. If the BeanDefinition already exists, then it will determine if it is allowed to override the BeanDefinition. If not, it will throw an exception. If BeanDefinition is allowed, then save the value again in beanDefinitionMap. If BeanDefinition is allowed, then save the value again in beanDefinitionMap. Overwrite the previous value.
  4. If BeanDefinition doesn’t exist, just register. Direct registration comes in two cases: the project is already running and the project is not yet running.
  5. If the project is already running, since beanDefinitionMap is a global variable, there may be concurrency issues, so lock handling is required. Otherwise, register directly, which means storing objects in beanDefinitionMap and beanName in beanDefinitionNames collection.

This is how the registerBeanDefinition method works.

Some people might say, well, this method is a BeanDefinition from beginning to end. What does it have to do with beans?

At first glance, it does not seem to be directly related to beans.

In fact, this relates to another problem, which is lazy loading of beans. The BeanDefinition is defined, and the Bean is not initialized until it is actually called. We can print the log in the constructor of the User class as follows:

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

    public User(a) {
        System.out.println("--------user init--------");
    }

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

    public String getUsername(a) {
        return username;
    }

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

    public String getAddress(a) {
        return address;
    }

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

As you can see in the figure below, when BeanDefinition is registered, the User is not initialized until the getBean method is called.

It is important to note that the ApplicationContext we use in our daily development is not lazy loading, as can be seen in Songo’s Spring Introduction video.www.bilibili.com/video/BV1Wv…”, songo will share with you in a later article in this series.

So what if you don’t want to load lazily? Of course there is.

4. Pre-register beans

In DefaultListableBeanFactory another preInstantiateSingletons method can registered Bean in advance, the method is declared in ConfigurableListableBeanFactory interface, DefaultListableBeanFactory class implements the ConfigurableListableBeanFactory interface and implementation methods in an interface:

@Override
public void preInstantiateSingletons(a) throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}
	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					finalFactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
					if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
						isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else{ getBean(beanName); }}}// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if(System.getSecurityManager() ! =null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code

The whole logic of the preInstantiateSingletons method is pretty simple, just iterate over beanNames, instantiate the qualified Bean, and notice that the preinstantiation means that before we call the getBean method, It calls getBean itself first.

We can call this method manually in our case:

DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username"."javaboy");
pvs.add("address"."www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
defaultListableBeanFactory.preInstantiateSingletons();
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
Copy the code

The User is now initialized before the getBean method is called, as shown below:

5.getBean

Another heavyweight in the DefaultListableBeanFactory is getBean method. But the real implementation is getBean method in the superclass AbstractBeanFactory DefaultListableBeanFactory, concrete implementation method is doGetBean, originally want to and you talk about the problem here, However, it turns out that this is a huge problem, and both BeanFactory and FactoryBean have not been shared with us yet, so we will postpone this topic and take it one point at a time.

6. Summary

Well, today first say so much, each source I try to configure a set of small cases to demonstrate, so as to avoid you see too boring, we continue next week ~

If you feel that there is a harvest, remember to click under the encouragement of Songko oh ~