The goal of this blog
1. Handwriting spring The whole process of circular dependence
2. spring How to solve the circular dependence
3. Why second level cache and third level cache
4. spring Have you solved the circular dependency of constructors
5. spring Have you solved the cyclic dependency in multiple cases .
One . What is circular dependence ?
As shown in the figure below :
A Class depends on B class , meanwhile B Class has dependencies A class . This is circular dependency , It forms a closed loop
Pictured above : A Rely on B, B At the same time, I rely on A and C , C Rely on A. This is also circular dependence . , It forms a closed loop
that , If a cyclic dependency occurs , spring How to solve the problem of circular dependency ?
Two . Simulate circular dependence
2.1 Recurring cyclic dependence
We define three classes :
1. The new class InstanceA
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class InstanceA { @Autowired private InstanceB instanceB; public InstanceA() { System.out.println(" call instanceA Constructor for "); } public InstanceA(InstanceB instanceB) { this.instanceB = instanceB; } public void say(){ System.out.println( "I am A"); } public InstanceB getInstanceB() { return instanceB; } public void setInstanceB(InstanceB instanceB) { this.instanceB = instanceB; } }
This is a InstanceA, It quoted InstanceB.
2. The new class instanceB
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class InstanceB { @Autowired private InstanceA instanceA; public InstanceB() { System.out.println(" call instanceB Constructor for "); } public InstanceA getInstanceA() { return instanceA; } public void setInstanceA(InstanceA instanceA) { this.instanceA = instanceA; } }
This is a InstanceB, It's quoted in it InstanceA
3: simulation spring How to create Bean Of
This has been said before , The post processor of the configuration class is loaded first , It will be parsed and put into the beanDefinitionMap in . Then load the configuration class , It is also parsed and put into beanDefinitionMap in . Finally, the configuration class is parsed . Let's simplify the first two steps , Put two classes into beanDefinitionMap in . It mainly simulates the third step of parsing the configuration class . In the process of parsing , obtain bean There will be a problem of circular dependence .
First step : Put two classes into beanDefinitionMap in
public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/** * Read bean Definition , Of course. spring According to the configuration Dynamic scan registered * * InstanceA and InstanceB It's all annotated @Component, therefore , stay spring Scan when reading configuration classes , We'll scan both of them BeanDefinitionMap in . * here , We omit this step , Direct will instanceA and instanceB Put it in BeanDefinitionMap in . */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // First step : Scan configuration class , Read bean Definition loadBeanDefinitions(); ...... }
The above code structure is very simple , If you look at the notes again, you should understand . Here's the simulation spring Put configuration class resolution into beanDefinitionMap The process of .
The second step : Loop creation bean
First , We already know , establish bean There are three steps : Instantiation , Attribute assignment , initialization .
When the attribute is assigned , It will determine whether other references are made Bean, If you quote , Then you need to build this Bean. Let's take a look at the code
/** * obtain bean, according to beanName obtain */ public static Object getBean(String beanName) throws Exception {/** * First step : Instantiation * We have a simulation here , Instantiation by reflection . It also calls the simplest parameterless constructor */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // Call a parameterless constructor to instantiate Object instanceBean = beanClass.newInstance(); /** * The second step : Attribute assignment * instanceA There is an attribute in this class , InstanceB. therefore , To get the first instanceB, Then judge whether there is any in the attribute header Autowired annotation . * Be careful : Here we're just judging if there's one Autowired annotation . spring It will also judge whether there is @Resource annotation . @Resource There are two other ways to annotate , One is name, One is type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // Determine whether each attribute has @Autowired annotation Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // Setting this property is accessible declaredField.setAccessible(true); // At this time, we need to build this attribute bean. /* * Get the name of the property * The truth , spring Here we will judge , It's based on the name , Or type , Or the constructor to get the class . * We simulate here , So it's simpler , Get it directly by name . */ String name = declaredField.getName(); /** * such , Here we get it instanceB Of bean */ Object fileObject = getBean(name); // Set the type for the property declaredField.set(instanceBean, fileObject); } } /** * The third step : initialization * Initialization is to set the class init-method. This can be set or not . We don't set it here */ return instanceBean; }
We see the code above .
First step : Instantiation : Use reflection , according to beanName Find and build an instance bean.
The second step : Attribute assignment : Determine whether there are @Autowired attribute , If you have this property , So it needs to be built bean. We found that InstanceA When you assign , It quoted InstanceB, So create it InstanceB, And create InstanceB When , Found that there are InstanceA, So I went to create A. And so on , Continue to judge . It's a dead circle . We can't get out of this ring . This is circular dependency
The third step : initialization : call init-method, This method is not necessary , therefore , We won't simulate it here
Take a look at the figure below
The red part forms a circular dependence .
4: Add level 1 cache , Solving the problem of circular dependency .
We know that there's a circular dependency on it . Actually , Our goal is simple , If a class has been created , Then please do not create .
therefore , We add one level cache
// First level cache private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); /** * obtain bean, according to beanName obtain */ public static Object getBean(String beanName) throws Exception { // Add an exit . Determine whether the entity class has been loaded Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * First step : Instantiation * We have a simulation here , Instantiation by reflection . It also calls the simplest parameterless constructor */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // Call a parameterless constructor to instantiate Object instanceBean = beanClass.newInstance(); /** * The second step : Put into L1 cache */ singletonObjects.put(beanName, instanceBean); /** * The third step : Attribute assignment * instanceA There is an attribute in this class , InstanceB. therefore , To get the first instanceB, Then judge whether there is any in the attribute header Autowired annotation . * Be careful : Here we're just judging if there's one Autowired annotation . spring It will also judge whether there is @Resource annotation . @Resource There are two other ways to annotate , One is name, One is type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // Determine whether each attribute has @Autowired annotation Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // Setting this property is accessible declaredField.setAccessible(true); // At this time, we need to build this attribute bean. /* * Get the name of the property * The truth , spring Here we will judge , It's based on the name , Or type , Or the constructor to get the class . * We simulate here , So it's simpler , Get it directly by name . */ String name = declaredField.getName(); /** * such , Here we get it instanceB Of bean */ Object fileObject = getBean(name); // Set the type for the property declaredField.set(instanceBean, fileObject); } } /** * Step four : initialization * Initialization is to set the class init-method. This can be set or not . We don't set it here */ return instanceBean; }
Or the acquisition above bean The process of , The difference is , Here we add and cache . When we get bean After the example , Put it in the cache . Before you need to create it next time , First go to the cache to judge , Does it already exist , without , Then create it again .
This creates the bean One more exit . It won't loop .
As shown in the figure above , stay @Autowired When , One more exit . Determine whether the class to be created already exists , If it exists , So just go back , Not creating
Although the first level cache is used to solve the problem of circular dependency , But in multithreading , This dependency can be problematic .
such as : There are two threads , Create at the same time instanceA and instanceB, instanceA and instanceB All quoted instanceC. They do it in sync , All to create instanceC. First A To create , A In instantiation instanceC After that, it will be put into the first level cache , Now , B Get it from the first level cache . I got instanceC It's incomplete . The following property assignment , Initialization has not been executed yet . therefore , We've added headphone buffers to solve this problem .
5. Increase L2 cache , Distinguish complete bean And pure bean.
public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // First level cache private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // Second level cache private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); /** * Read bean Definition , Of course. spring According to the configuration Dynamic scan registered * * InstanceA and InstanceB It's all annotated @Component, therefore , stay spring Scan when reading configuration classes , We'll scan both of them BeanDefinitionMap in . * here , We omit this step , Direct will instanceA and instanceB Put it in BeanDefinitionMap in . */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // First step : Scan configuration class , Read bean Definition loadBeanDefinitions(); // The second step : Loop creation bean for (String key: beanDefinitionMap.keySet()) { // for the first time : key yes instanceA, So create it first A class getBean(key); } // test : See if it works InstanceA instanceA = (InstanceA) getBean("instanceA"); instanceA.say(); } /** * obtain bean, according to beanName obtain */ public static Object getBean(String beanName) throws Exception { // Add an exit . Determine whether the entity class has been loaded Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * First step : Instantiation * We have a simulation here , Instantiation by reflection . It also calls the simplest parameterless constructor */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // Call a parameterless constructor to instantiate Object instanceBean = beanClass.newInstance(); /** * The second step : Put into L2 cache */ earlySingletonObjects.put(beanName, instanceBean); /** * The third step : Attribute assignment * instanceA There is an attribute in this class , InstanceB. therefore , To get the first instanceB, Then judge whether there is any in the attribute header Autowired annotation . * Be careful : Here we're just judging if there's one Autowired annotation . spring It will also judge whether there is @Resource annotation . @Resource There are two other ways to annotate , One is name, One is type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // Determine whether each attribute has @Autowired annotation Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // Setting this property is accessible declaredField.setAccessible(true); // At this time, we need to build this attribute bean. /* * Get the name of the property * The truth , spring Here we will judge , It's based on the name , Or type , Or the constructor to get the class . * We simulate here , So it's simpler , Get it directly by name . */ String name = declaredField.getName(); /** * such , Here we get it instanceB Of bean */ Object fileObject = getBean(name); // Set the type for the property declaredField.set(instanceBean, fileObject); } } /** * Step four : initialization * Initialization is to set the class init-method. This can be set or not . We don't set it here */ /** * The second step : Put into L1 cache */ singletonObjects.put(beanName, instanceBean); return instanceBean; } /** * Determine whether it is the exit of a circular reference . * @param beanName * @return */ private static Object getSingleton(String beanName) { // Go to level 1 cache first , If the first level cache is not obtained , Get it from the secondary cache if (singletonObjects.containsKey(beanName)) { return singletonObjects.get(beanName); } else if (earlySingletonObjects.containsKey(beanName)){ return earlySingletonObjects.get(beanName); } else { return null; } } }
As shown in the figure above , A second level cache is added . First , build instanceBean in the future , Put it directly into the L2 cache . It's just a pure one bean, The attribute has not been assigned a value , initialization . After assigning a value to a property , After initialization , Put it into the first level cache .
We determine if there is an instance in the cache bean When , First go to the first level cache to determine whether there is a complete bean, without , Go to the level 2 cache to determine whether this has been instantiated bean.
summary : The role of first level cache and second level cache
First level cache : Solving the problem of circular dependency
Second level cache : Create instance in bean And there's a gap between putting it in the first level cache . If you take an instance from the first level cache in between , It must be back null Of . To avoid this problem , Second level cache is added .
We all know spring There is a level 1 cache in , Second level cache , Three level cache . We know the role of the first level cache and the second level cache , So what's the use of level 3 caching ?
6. Increase level 3 cache
What's the use of level 3 caching ? There are different opinions on this issue , It is said that agency , Have said AOP. Actually AOP We can solve the problem of secondary cache . So let's see AOP How to use L2 cache to solve this problem .
establish AOP A dynamic proxy ( It's not coupled , Using decoupling , adopt BeanPostProcessor bean To create a ). Talked about before , Here's the picture
After initialization , call Bean To create a post processor AOP Dynamic proxy for
Pictured above . We're creating bean When , There will be a lot of Bean Post processor for BeanPostProcessor. If there is AOP, When will it be created ? After initialization , call BeanPostProcessor Create a dynamic agent .
Combine the above code , Let's think about it , In fact, it is too late to create a dynamic proxy after initialization . Why? ? because , If there is circular dependence , Called after initialization , That's not a dynamic proxy . In fact, we should be after instantiation , Call before entering the two level cache.
Interview questions : Creating bean When , Where is the dynamic proxy created , How should I answer this ? A lot of people would say after initialization , Or after instantiation . In fact, to be more precise , There are two situations : The first is called after initialization. . The second is the emergence of circular dependence , It will be called after instantiation.
What we said above is the second case . in other words , Normally, it is called after initialization. , But if there's cyclic dependency , It will be called after instantiation. .
Let's take a look at how to add dynamic proxies to the secondary cache .
First , We have circular dependence here , So put the dynamic proxy after instantiation ,
/** * obtain bean, according to beanName obtain */ public static Object getBean(String beanName) throws Exception { // Add an exit . Determine whether the entity class has been loaded Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * First step : Instantiation * We have a simulation here , Instantiation by reflection . It also calls the simplest parameterless constructor */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // Call a parameterless constructor to instantiate Object instanceBean = beanClass.newInstance(); /** * establish AOP A dynamic proxy ( It's not coupled , Using decoupling , adopt BeanPostProcessor bean From the post processor . Talked about before , * After initialization , call Bean To create a post processor AOP Dynamic proxy for ) */ instanceBean = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, "instanceA"); /** * The second step : Put into L2 cache */ earlySingletonObjects.put(beanName, instanceBean); /** * The third step : Attribute assignment * instanceA There is an attribute in this class , InstanceB. therefore , To get the first instanceB, Then judge whether there is any in the attribute header Autowired annotation . * Be careful : Here we're just judging if there's one Autowired annotation . spring It will also judge whether there is @Resource annotation . @Resource There are two other ways to annotate , One is name, One is type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // Determine whether each attribute has @Autowired annotation Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // Setting this property is accessible declaredField.setAccessible(true); // At this time, we need to build this attribute bean. /* * Get the name of the property * The truth , spring Here we will judge , It's based on the name , Or type , Or the constructor to get the class . * We simulate here , So it's simpler , Get it directly by name . */ String name = declaredField.getName(); /** * such , Here we get it instanceB Of bean */ Object fileObject = getBean(name); // Set the type for the property declaredField.set(instanceBean, fileObject); } } /** * Step four : initialization * Initialization is to set the class init-method. This can be set or not . We don't set it here */ // Timing of normal dynamic proxy creation /** * Step five : Put into L1 cache */ singletonObjects.put(beanName, instanceBean); return instanceBean; }
This is just a simple simulation of the dynamic proxy .
We know there are two things about dynamic proxies . If it is a normal class, the dynamic proxy is executed after initialization , If it's cyclic dependency , So the dynamic proxy is after instantiation .
The above is created after instantiation proxy Incomplete code for , Why not complete , Because there is no judgment whether it is cyclic dependence .
We simply simulate the implementation of a dynamic proxy .
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { /** * hypothesis A Hit by tangent point Proxy needs to be created @PointCut("execution(* *..InstanceA.*(..))") * @param bean the raw bean instance * @param beanName the name of the bean * @return * @throws BeansException */ @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { // hypothesis A Hit by tangent point Proxy needs to be created @PointCut("execution(* *..InstanceA.*(..))") /** * here , We make a simple and direct judgment bean Is it right? InstanceA example , If it is , Create a dynamic proxy . * There's no parsing of pointcuts , The analytic pointcut is AspectJ What to do . */ if (bean instanceof InstanceA) { JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean); return jdkDynimcProxy.getProxy(); } return bean; } }
This is a direct judgment , If bean yes InstanceA Example , So call bean Dynamic proxy for . The simple logic of dynamic proxy is : profiling aspect , Then create the class , Add a new class if it doesn't exist , If it exists, it is not created , Take it out and return it .
Let's take a look at the dynamic proxy , After instantiation . establish AOP, however , Create... Here AOP The condition of dynamic proxy is circular dependency .
problem 1: So how to judge whether it is cyclic dependence ?
In L2 cache bean No null.
If a class is in the process of creating , Will be put into the L2 cache , If it is completely created , Will be put into the first level cache , Then delete the L2 cache . therefore , If the bean As long as there is a , This shows that this class is being created , There's a circular dependency .
problem 2: When to judge ?
belong getSingleton() Judge whether it is circular dependence . Because at this time, we just judge that the level 2 cache is in bean Is it empty .
/** * Determine whether it is the exit of a circular reference . * @param beanName * @return */ private static Object getSingleton(String beanName) { // Go to level 1 cache first , If the first level cache is not obtained , Get it from the secondary cache if (singletonObjects.containsKey(beanName)) { return singletonObjects.get(beanName); } else if (earlySingletonObjects.containsKey(beanName)){ /** * First creation bean It's normal instanceBean. He's not cyclical . Come in and judge for the second time , This bean It already exists , That means it's cyclic dependency * At this point, it is created through a dynamic proxy bean. And then put this bean In the second level cache into the original instanceBean. */ Object obj = new JdkProxyBeanPostProcessor() .getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName); earlySingletonObjects.put(beanName, obj); return earlySingletonObjects.get(beanName); } else { return null; } }
So we're done with circular dependency AOP The creation of . This is created in the L2 cache AOP,
problem 3: Does this mean that there is no need for Level 3 caching ?
that , To find out . Here are two questions :
problem 1: We found that when we create a dynamic proxy , What we use bean Post processor for JdkProxyBeanPostProcessor. It's a bit out of line with the rules ,
because , spring stay getBean() They didn't use it Bean Post processor for , But in createBean() I only used it when I was in bean Post processor for .
problem 2: If A yes AOP, He's always been , It should also be created at the beginning . Using this method , The result was created for the first time bean No AOP A dynamic proxy .
For the first question : We want to create it at instantiation time AOP, But the specific judgment is in getSingleton() Judgment in method . This is achieved by three-level cache . There is a three-level interface called hook . Method is executed when it is called later .
For the second question : Our level 2 cache can't be saved directly instanceBean The instance , Add a parameter , Used to mark that the current class is a class being created . This is how to judge circular dependence .
Let's take a look at the three caches and one identifier created
// First level cache private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // Second level cache : In order to mature bean And pure bean Separate . Avoid reading incomplete bean. private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // Three level cache : private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>(); // The identity of the circular dependency --- The instance currently being created bean private static Set<String> singletonsCurrectlyInCreation = new HashSet<>();
Then let's look at the exit of circular dependence
/** * Determine whether it is the exit of a circular reference . * @param beanName * @return */ private static Object getSingleton(String beanName) { // Go to level 1 cache first Object bean = singletonObjects.get(beanName); // Not in L1 cache , But what is being created bean There are , Description is circular dependency if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // If there is no in the L2 cache , Take it from level 3 cache if (bean == null) { // Fetch from L3 cache ObjectFactory objectFactory = singletonFactories.get(beanName); if (objectFactory != null) { // This is where the real dynamic proxy is created . Object obj = objectFactory.getObject(); // It is then put into the L2 cache . Because if you have multiple dependencies , Go to the second level cache to judge . If there is already one, it will not be created again earlySingletonObjects.put(beanName, obj); } } } return bean; }
Here's the logic , Go to level 1 cache first , The first grade is mature bean, That is to say, he has completed the property assignment and initialization . If the first level cache does not , The class ID being created is true, This shows that the class is being created , This is a circular dependency . At this time, the data is fetched from the secondary cache , When is the data in the L2 cache put in , The dynamic proxy is put in after the dynamic proxy is created from the third level cache . If the L2 cache is empty , Indicates that no dynamic proxy has been created , At this time, go to level 3 cache , Then create a dynamic proxy . After it is created, it will be put into the secondary cache , You don't have to create it later .
The completed code is as follows :
package com.lxl.www.circulardependencies; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // First level cache private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // Second level cache : In order to mature bean And pure bean Separate . Avoid reading incomplete bean. private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // Three level cache : private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>(); // The identity of the circular dependency --- The instance currently being created bean private static Set<String> singletonsCurrectlyInCreation = new HashSet<>(); /** * Read bean Definition , Of course. spring According to the configuration Dynamic scan registered * * InstanceA and InstanceB It's all annotated @Component, therefore , stay spring Scan when reading configuration classes , We'll scan both of them BeanDefinitionMap in . * here , We omit this step , Direct will instanceA and instanceB Put it in BeanDefinitionMap in . */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // First step : Scan configuration class , Read bean Definition loadBeanDefinitions(); // The second step : Loop creation bean for (String key: beanDefinitionMap.keySet()) { // for the first time : key yes instanceA, So create it first A class getBean(key); } // test : See if it works InstanceA instanceA = (InstanceA) getBean("instanceA"); instanceA.say(); } /** * obtain bean, according to beanName obtain */ public static Object getBean(String beanName) throws Exception { // Add an exit . Determine whether the entity class has been loaded Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } // Mark bean Creating if (!singletonsCurrectlyInCreation.contains(beanName)) { singletonsCurrectlyInCreation.add(beanName); } /** * First step : Instantiation * We have a simulation here , Instantiation by reflection . It also calls the simplest parameterless constructor */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // Call a parameterless constructor to instantiate Object instanceBean = beanClass.newInstance(); /** * The second step : Put into Level 3 cache * every time createBean Will be put into the third level cache . getObject It's a hook method . It's not called here . * When will it be called ? * stay getSingleton() Fetching data from L3 cache , Call to create a dynamic proxy */ singletonFactories.put(beanName, new ObjectFactory() { @Override public Object getObject() throws BeansException { return new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName); } }); //earlySingletonObjects.put(beanName, instanceBean); /** * The third step : Attribute assignment * instanceA There is an attribute in this class , InstanceB. therefore , To get the first instanceB, Then judge whether there is any in the attribute header Autowired annotation . * Be careful : Here we're just judging if there's one Autowired annotation . spring It will also judge whether there is @Resource annotation . @Resource There are two other ways to annotate , One is name, One is type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // Determine whether each attribute has @Autowired annotation Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // Setting this property is accessible declaredField.setAccessible(true); // At this time, we need to build this attribute bean. /* * Get the name of the property * The truth , spring Here we will judge , It's based on the name , Or type , Or the constructor to get the class . * We simulate here , So it's simpler , Get it directly by name . */ String name = declaredField.getName(); /** * such , Here we get it instanceB Of bean */ Object fileObject = getBean(name); // Set the type for the property declaredField.set(instanceBean, fileObject); } } /** * Step four : initialization * Initialization is to set the class init-method. This can be set or not . We don't set it here */ /** * Step five : Put into L1 cache * * In this case, the secondary cache stores dynamic agents , Then the first level cache must also store instances of dynamic agents . * Extract instance from L2 cache , Put into the first level cache */ if (earlySingletonObjects.containsKey(beanName)) { instanceBean = earlySingletonObjects.get(beanName); } singletonObjects.put(beanName, instanceBean); return instanceBean; } /** * Determine whether it is the exit of a circular reference . * @param beanName * @return */ private static Object getSingleton(String beanName) { // Go to level 1 cache first , Object bean = singletonObjects.get(beanName); // Not in L1 cache , But what is being created bean There are , Description is circular dependency if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // If there is no in the L2 cache , Take it from level 3 cache if (bean == null) { // Fetch from L3 cache ObjectFactory objectFactory = singletonFactories.get(beanName); if (objectFactory != null) { // This is where the real dynamic proxy is created . Object obj = objectFactory.getObject(); // It is then put into the L2 cache . Because if you have multiple dependencies , Go to the second level cache to judge . If there is already one, it will not be created again earlySingletonObjects.put(beanName, obj); } } } return bean; } }