A process of Spring Bean from scratch

There is a beginning but no end

Experienced the history of java development, from the early writing of native servlet s, self-developed mvc and jdbc tools, and the use of open-source framework struts, hibernate, jsp, spring, springmvc, freemaker, springboot, to the final front and back-end separate development, the first development tool is editplus. Relatively speaking, now the development is very good, and the framework Ecology (only spring ecology has been inherited, and others have become history). By the end of the spring ecosystem, its system is too large, sweeping across countries, and the chance of technology transformation of project products is very small.

At first, I did Spring related development, wrote a lot of xml configuration, and later changed to annotation development. Although I can work, I don't know what its internal principle and design concept are. With the accumulation of time, I need to do sping extension or integration, so I need to study how to realize the source level. I'll start with how to create a Spring Bean. Don't underestimate a Bean. Many code farmers can't explain its creation process.

A brief introduction to Spring framework is an open source J2EE Application framework, by Rod Johnson Initiated, is a lightweight container that manages the bean's life cycle. Spring solves many common problems encountered by developers in J2EE development, and provides powerful IOC AOP And Web MVC. Spring can be used to build applications alone, can also be combined with Struts, Webwork, Tapestry and many other Web frameworks, and can be combined with Swing and other desktop applications AP. Therefore, spring can be applied not only to JEE applications, but also to desktop applications and small applications. The spring framework consists of seven parts: Spring Core, Spring AOP, Spring ORM, Spring DAO, Spring Context, Spring Web and Spring Web MVC.


It's the Beans in the picture. Let's get to the main point. Look carefully. It's very important

Before introducing bean s, let's talk about ioc and context context (it can be understood as a host environment)

ioc is the abbreviation of Inversion of Control, which is called Inversion of Control in the industry. In the early stage of development, we need to create an object by ourselves. But with ioc, we don't need to

I have new objects, and let spring ioc container be responsible for the creation and management of objects. In a word, the function of ioc reverses the dependency of objects, and ioc container manages objects.

Trying to understand bean, ioc and context is very important for learning spring ecology in the future.

 

There are two types of spring beans: ordinary beans and factory beans, but there are many types of processing

1, Common bean s (take xml configuration for example, more and more people are using annotations now)

Prepare raw materials

Spring bean.xml file (take the demonstration as an example)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
	<bean id="user" class="spring.model.User">
		<property name="id" value="1"/>
		<property name="name" value="dongguangming"/>
		<property name="age" value="99"/>
	</bean>
    
</beans>

      User.java

public class User {

	private int id;
	private String name;
	private int age;
   //set,get omitted
}

Test class

/**
 * 
 * @author dgm
 * @describe "xml bean"
 * @date 2020 April 16, 2016
 */
public class XMLConfigurationBeanApp {
	public static void main(String[] args) {
		/*DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
		
		reader.loadBeanDefinitions("conf/spring-bean.xml");*/
		 ApplicationContext applicationContext = 
	                new ClassPathXmlApplicationContext("conf/spring-bean.xml");
		User user1 = applicationContext.getBean("user", User.class);
		User user2 = (User) applicationContext.getBean("user");
		System.out.println(user1);
		System.out.println(user2);
		System.out.println(user1==user2);

		//Because it is a common bean, there is no need to write this in case of exception. I test two different types of beans
		System.out.println(applicationContext.getBean("&user"));
	}
}

Output results


Parse the initialization process from this line of code

 ApplicationContext applicationContext = 
	                new ClassPathXmlApplicationContext("spring-bean.xml");

Speaking, let's see how much spring has done

Three stages: bean parsing, bean instantiation, bean initialization and destruction

1. bean resolution definition registration stage

Early developers all know that configuration files are mostly xml files (now people like annotation parsing). They need to parse the contents of xml files into java corresponding classes, or dom parsing for short, such as


spring is the same. At the beginning, a lot of xml file parsing work was carried out, and the class mapping corresponding to java is good

File path org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException


/**
	 * Actually load bean definitions from the specified XML file.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
            //xml document parsing
			Document doc = doLoadDocument(inputSource, resource);
            //Call register bean definition
			return registerBeanDefinitions(doc, resource);
		}
}

//File path org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
        //Call bean registration
		documentReader.registerBeanDefinitions(doc, 
createReaderContext(resource));

		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

. . . . . . A lot of nesting

//File path org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
                //Call registration tool registration
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

//File path org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException


@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 oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
            //Finally, the measurement is successful, a data structure
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
                   //Ditto
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

This is just a bean definition (there are many bean definitions registered during the development process). It is very important to do so much work to assemble this data structure: this.beanDefinitionMap.put(beanName, beanDefinition); it is defined as follows:

/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

Part of the attributes of the beanDefinition, of course, is a structure for assembling data

​ 

2. Bean instantiation and initialization phase

When called

ClassPathXmlApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 861	

{
//Partial code
// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
				@Override
				public String resolveStringValue(String strVal) {
					return getEnvironment().resolvePlaceholders(strVal);
				}
			});
		}

// Instantiate all remaining (non-lazy-init) singletons.
//There are so many things in it
		beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons core code:

@Override
	public void preInstantiateSingletons() throws BeansException {


		// 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)) {
                    //Factory beans (there are many in the system, but you can also write them yourself), implement FactoryBean interface or inherit AbstractFactoryBean
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
                    //Ordinary bean, human implemented, does not implement FactoryBean interface or does not inherit AbstractFactoryBean
					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(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

 

0: DefaultListableBeanFactory(AbstractBeanFactory).getBean(String)

1 DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 303    
2. Defaultlistablebeanfactory (defaultsingletonbeanregistry). getSingleton (string) line: 231, first getSingleton
3 Object org.springframework.beans.factory.support.AbstractBeanFactory.createBean(String beanName, RootBeanDefinition mbd, Object[] args) 

To split:

4. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd)

Determine whether there is an instantiaawarebeanpostprocessor, applybeanpostprocessor sbeforeinstance() > postprocessbeforeinstance(),

applyBeanPostProcessorsAfterInitialization()>postProcessAfterInitialization().

The chance to return the proxy object. Once returned, there is no following. aop proxy can be generated from this

If you return, skip to stage 10
5 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, 4 RootBeanDefinition mbd, Object[] args) is very important
6 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args). Instantiation is complete. Please continue to see the source code for details. Note that there are more than one instantiation methods: constructor, factory method,

cglid agent
7 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(beanName, mbd, instanceWrapper), please continue to see the source code for details, the following is the part

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}


org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(String beanName, Object bean, RootBeanDefinition mbd) , initialization complete

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run() {
					invokeAwareMethods(beanName, bean);
					return null;
				}
			}, getAccessControlContext());
		}
		else {
            //Implement the call of the suffix Aware interface at this stage
            //BeanNameAware
            //BeanClassLoaderAware
            //BeanFactoryAware
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
            //Implement the BeanPostProcessor interface
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
            //Call the initialization method to determine whether the InitializingBean is implemented. The method name is afterpropertieset(), and the custom initialization method is also here
			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()) {
             //Implement the BeanPostProcessor interface
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

10 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory The second getSingleton, but the implementation method is different

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry


/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	/** Set of registered singletons, containing the bean names in registration order */
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

Note that the space is limited: some core methods are listed, in fact, there are many, because the code is too much, it will not be pasted

Factory bean

The Bean implements the FactoryBean interface

public class UserFactoryBean<User> implements FactoryBean<User> {

	private User user;
	
	
	/**
	 * @return the user
	 */
	public User getUser() {
		return user;
	}

	
	/**
	 * @param user the user to set
	 */
	public void setUser(User user) {
		this.user = user;
	}

	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.FactoryBean#getObjectType()
	 */
	@Override
	public Class<?> getObjectType() {
		return user.getClass();
	}


	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.FactoryBean#getObject()
	 */
	@Override
	public User getObject() throws Exception {
		// TODO Auto-generated method stub
		return user;
	}

}

It does not implement the FactoryBean interface in the same way as the previous one. Generally speaking, it is the same. No more details will be given

/**
	 * Obtain an object to expose from the given FactoryBean.
	 * @param factory the FactoryBean instance
	 * @param beanName the name of the bean
	 * @param shouldPostProcess whether the bean is subject to post-processing
	 * @return the object obtained from the FactoryBean
	 * @throws BeanCreationException if FactoryBean object creation failed
	 * @see org.springframework.beans.factory.FactoryBean#getObject()
	 */
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (object != null && shouldPostProcess) {
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
						}
						this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
					}
				}
				return (object != NULL_OBJECT ? object : null);
			}
		}
		else {
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (object != null && shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

Sequence diagram:


3. bean destruction

Three manifestations:

3.1 the DisposableBean interface corresponding to InitializingBean,

3.2 custom method, destroy method = "destroyxml"

3.3 @ PreDestroy corresponding to @ PostConstruct

Destruction sequence is as shown in the figure


The code doesn't give an example.

 

Summary: in fact, you can use new user (6, "dongguangming", 99), but it has nothing to do with spring. Spring is very powerful. There are opportunities to transform the process of participating bean s in all stages, and the degree of composition is also very high

Remember that spring beans: resolve, register, instantiate, initialize and destroy to develop or integrate third-party components (such as mybatis, dubbo, email, zk, redis, etc.) into the spring ecosystem

 

reference resources:

0 Introduction to spring framework https://www.ibm.com/developerworks/cn/java/wa-spring1/

1. What is spring bean https://www.awaimai.com/2596.html

2  what-in-the-world-are-spring-beans https://stackoverflow.com/questions/17193365/what-in-the-world-are-spring-beans

2. Spring Bean Lifecycle https://www.benchresources.net/spring-bean-lifecycle/

3. Detailed tutorial on Redis caching in the SpringBoot series  https://laptrinhx.com/detailed-tutorial-on-redis-caching-in-the-springboot-series-3352915639/

4. Spring Bean creation process http://www.programmersought.com/article/55942589567/

5.  Inversion of Control Containers and the Dependency Injection pattern https://martinfowler.com/articles/injection.html

Keywords: Java Spring xml SpringBoot

Added by robinas on Fri, 24 Apr 2020 09:45:41 +0300