3.1 spring 5 source code series -- handwritten code simulation of spring loop dependency

The sun in full bloom 2020-11-07 20:15:23
spring source code series handwritten


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;
}
}

 

版权声明
本文为[The sun in full bloom]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云