Spring循环依赖
文章目录
- Spring循环依赖
- 前言
- 一、搭积木
- 1.1 定义循环依赖Bean
- 1.2 手写第一版本
- 1.3 手写第二版本
- 二、问题解决
- 二级缓存解决不成熟bean的情况
- 二级缓存完美解决
- Spring的纠结点
- 三、完美解决
前言
循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
在本博客,我将手写实现一下Spring的循环依赖。试试水
一、搭积木
1.1 定义循环依赖Bean
1、student类
package com.jztai.spring.circledependence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Teacher {
@Autowired
private Student student;
}
2、Student
package com.jztai.spring.circledependence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Student {
@Autowired
private Teacher teacher;
}
3、动态代理
package com.jztai.spring.circledependence;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
// 简单化,直接返回对象
return bean;
}
}
1.2 手写第一版本
第一版本是入门,但是会出现死循环。
package com.jztai.spring.circledependence;
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.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第一版本的循环依赖,很明显会出现死循环
*/
public class CircleDev1 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean
*
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Class beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 2、属性复制
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
// 4、添加到一级缓存
singletonObjects.put(beanName, beanInstance);
return beanInstance;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
}
}
1.3 手写第二版本
1.2出现的问题是死循环,解决他的方式就是先看看单例池里面有没有,有的话,直接获取单例池的即可。但是这个版本的问题是导致Spring获取到了不完整的bean对象。这个状态称之为纯净态的bean。
package com.jztai.spring.circledependence;
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.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第二版本的循环依赖,加入了判断,如果单例池中有就直接返回
* 但是这个版本会导致获取不成熟的bean(纯净态的bean)。循环依赖的属性得不到注入。
*/
public class CircleDev2 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean,先看看单例池有没有,有的话就返回,
* 但是版本一的代码会出现单例池无法put的情况,因为循环依赖到不了初始化的那个阶段,所以将put提前。
*
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 0、看看单例池有没有
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Class<?> beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 4、添加到一级缓存
singletonObjects.put(beanName, beanInstance);
// 2、属性赋值
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
return beanInstance;
}
private static Object getSingleton(String beanName) {
if (singletonObjects.containsKey(beanName)) {
return singletonObjects.get(beanName);
}
return null;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
Student student = (Student) singletonObjects.get("student");
System.out.println(student.toString());
}
}
二、问题解决
二级缓存解决不成熟bean的情况
上面的版本2能实现循环依赖,但是会造成获取到不成熟的Bean,因为A创建完成加入到一级缓存了,此时A里面的属性B还没有赋值呢,此时如果读取A的话,A就是不完整的bean。所以此时二级缓存上线了。即先去一级缓存拿,如果一级缓存没有就去二级缓存拿,总之这个缓存就是为了解决纯净bean的问题,将完整bean和纯净bean分离开来,避免读取到不完整的bean。引入二级缓存之后其实就可以解决循环依赖的问题了。
package com.jztai.spring.circledependence;
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.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第三版本的循环依赖,引入二级缓存,解决获取不到成熟Bean的问题。
* 一级缓存有的话,先去一级缓存拿,如果一级缓存没有的话,就去二级缓存拿。
*/
public class CircleDev3 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池,存放成熟Bean
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,为了将成熟bean和纯净bean分离开,避免读取到不完整的bean。
private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean,先看看单例池有没有,单例池没有,看看二级缓存有没有,如果有的话去二级缓存拿
* 一级缓存存成熟bean,二级缓存存纯净bean。
* 如果A在创建依赖B的时候发现二级缓存有,去拿二级缓存中的B给自己注入,注入之后A就成熟了。
* 此时再循环到B的时候,注入A即可,A是成熟的了,完美解决循环依赖
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 0、看看单例池有没有
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Class<?> beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 4、添加到二级缓存
earlySingletonObjects.put(beanName, beanInstance);
// 2、属性赋值
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
// 4、添加到一级缓存
singletonObjects.put(beanName, beanInstance);
return beanInstance;
}
private static Object getSingleton(String beanName) {
if (singletonObjects.containsKey(beanName)) {
return singletonObjects.get(beanName);
} else if (earlySingletonObjects.containsKey(beanName)) {
return earlySingletonObjects.get(beanName);
}
return null;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
Student student = (Student) singletonObjects.get("student");
System.out.println(student.toString());
}
}
二级缓存完美解决
只要在初始化Bean之后,创建动态代理,就可以完美实现Spring的循环依赖。其实从这开始要注意,二级缓存是不清理的,为的就是在循环依赖的时候有多重依赖问题,好几个类依赖我,不能每次都从三级缓存中创建一份,所以当二级缓存没有的时候,去三级缓存拿,拿出来之后删除三级缓存,然后加入到二级缓存,为的是多个依赖我,能从二级缓存中拿
package com.jztai.spring.circledependence;
import org.springframework.beans.factory.ObjectFactory;
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.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第四版本的循环依赖,二级缓存完美解决循环依赖,包括Aop
*/
public class CircleDev4 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池,存放成熟Bean
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,为了将成熟bean和纯净bean分离开,避免读取到不完整的bean。
private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean,先看看单例池有没有,单例池没有,看看二级缓存有没有,如果有的话去二级缓存拿
* 一级缓存存成熟bean,二级缓存存纯净bean。
* 如果A在创建依赖B的时候发现二级缓存有,去拿二级缓存中的B给自己注入,注入之后A就成熟了。
* 此时再循环到B的时候,注入A即可,A是成熟的了,完美解决循环依赖
*
* AOP动态代理咋解决
* 直接在实例化之后,调用Bean的后置处理器,去解析Pointcut,然后创建动态代理即可。
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 0、看看单例池有没有
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 给beanInstance创建动态代理
beanInstance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
// 4、添加到二级缓存
earlySingletonObjects.put(beanName, beanInstance);
// 2、属性赋值
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
// 初始化之后,Spring也进行了动态代理的创建
// 4、添加到一级缓存
singletonObjects.put(beanName, beanInstance);
return beanInstance;
}
private static Object getSingleton(String beanName) {
if (singletonObjects.containsKey(beanName)) {
return singletonObjects.get(beanName);
} else if (earlySingletonObjects.containsKey(beanName)) {
return earlySingletonObjects.get(beanName);
}
return null;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
Student student = (Student) singletonObjects.get("student");
System.out.println(student.toString());
}
}
Spring的纠结点
其实是用二级缓存就可以完美实现Spring的循环依赖。包括动态代理,但是Spring还是希望正常的Bean(没有循环依赖的bean)是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建。 ,上面的版本,正常的bean也在实例化bean之后进行创建了。改一下,将实例化之后创建的动态代理去掉,在初始化之后创建动态代理,然后循环依赖的时候,在一级缓存中拿不到,去二级缓存中拿的时候,创建动态代理,去二级缓存拿就说明此时是处于循环依赖的。这样就完美解决了在循环依赖的情况下,实例化后创建动态代理;以及没有循环依赖的bean在初始化之后创建动态代理。
package com.jztai.spring.circledependence;
import org.springframework.beans.factory.ObjectFactory;
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.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第五版本的循环依赖
* Spring还是希望正常的Bean是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建
* 要判断是否是循环依赖。二级缓存如果有的话,就代表当前处于循环依赖状态。
* 但是如果二级缓存有的话,getBean函数就返回了,所以直接将创建动态代理的代码放在getSigton函数里面。
* 所以其实只用耳机缓存就可以完美实现动态代理
*/
public class CircleDev5 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池,存放成熟Bean
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,为了将成熟bean和纯净bean分离开,避免读取到不完整的bean。
private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 假设Student使用了AOP,@PointCut(""),要给Student创建动态代理
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean,先看看单例池有没有,单例池没有,看看二级缓存有没有,如果有的话去二级缓存拿
* 一级缓存存成熟bean,二级缓存存纯净bean。
* 如果A在创建依赖B的时候发现二级缓存有,去拿二级缓存中的B给自己注入,注入之后A就成熟了。
* 此时再循环到B的时候,注入A即可,A是成熟的了,完美解决循环依赖
*
* AOP动态代理咋解决
* 直接在实例化之后,调用Bean的后置处理器,去解析Pointcut,然后创建动态代理即可。
*
* Spring还是希望正常的Bean是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建
* 要判断是否是循环依赖。二级缓存如果有的话,就代表当前处于循环依赖状态。
* 但是如果二级缓存有的话,getBean函数就返回了,所以直接将创建动态代理的代码放在getSigton函数里面。
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 0、看看单例池有没有
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 2、属性赋值
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
// 初始化之后,Spring也进行了动态代理的创建
// 4、添加到一级缓存
singletonObjects.put(beanName, beanInstance);
return beanInstance;
}
private static Object getSingleton(String beanName) {
if (singletonObjects.containsKey(beanName)) {
return singletonObjects.get(beanName);
} else if (earlySingletonObjects.containsKey(beanName)) {
// Spring还是希望正常的Bean是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建
// 要判断是否是循环依赖。二级缓存如果有的话,就代表当前处于循环依赖状态。
// 给beanInstance创建动态代理
Object beanInstance = earlySingletonObjects.get(beanName);
if(beanInstance isinstanceof JdkProxyBeanPostProcessor) return beanInstance;
beanInstance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
// 4、添加到二级缓存,如果有其他的也依赖于当前的对象,可以只拿到一个单例的动态代理。
earlySingletonObjects.put(beanName, beanInstance);
return beanInstance;
}
return null;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
Student student = (Student) singletonObjects.get("student");
System.out.println(student.toString());
}
}
三、完美解决
其实通过二级缓存就可以实现Spring的循环依赖了。但是Spring为了解耦,在上面的方法里面getSington方法里面即获取Bean又创建Bean,代码看起来不优美,所以引入了三级缓存。引入三级缓存之后,三级缓存是工厂的缓存,里面定义着创建动态代理的代码;因为会出现多个依赖问题,比如A、B、C、D,四个相互依赖,那么B再注入A的时候,给A创建了动态代理,C在注入A的时候,不能再次创建动态代理了,所以就用二级缓存来缓存动态代理对象,一级缓存还是存成熟的Bean。二级缓存的作用变了之后,没办法之后当前是否是循环依赖了,所以引入了Set集合来标识当前正在创建的Bean,当再次创建时,Set集合里面有该Bean就代表当前是循环依赖。代码如下:
package com.jztai.spring.circledependence;
import org.springframework.beans.factory.ObjectFactory;
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.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Written by MaShanTao
* 第6版本的循环依赖,引入三级缓存,为了代码解耦
* Spring还是希望正常的Bean是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建
* 要判断是否是循环依赖。二级缓存如果有的话,就代表当前处于循环依赖状态。
* 但是如果二级缓存有的话,getBean函数就返回了,所以直接将创建动态代理的代码放在getSigton函数里面。
* 所以其实只用二级缓存就可以完美实现动态代理。
* <p>
* 三级缓存是Map<String, ObjectFactory<?>>,ObjectFactory就是一个函数式接口,用来存创建动态代理的执行单元。
* 三级缓存创建代理对象之后,将代理对象放到二级缓存。二级缓存不存纯净对象了。
* 如果A,B,C互相依赖的话,A在创建B的时候生成代理对象了,此时C再去注入B的时候不能让他在创建一次
* 所以就将代理对象存二级缓存,C再注入B的时候,可以直接去二级缓存拿,避免重复创建。
* 而二级缓存不存纯净对象之后,就不能知道是否是循环依赖了。所以引入了Set标识循环依赖
* <p>
* A注入的时候,去创建B,B创建的时候,利用三级缓存创建A的动态代理,并将A的动态代理注入到B,将代理对象村存到二级缓存
* 此时B创建完之后回到A,A还是纯净对象,应该去二级缓存中拿代理对象。
*/
public class CircleDev6 {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 大名鼎鼎的单例缓存池,存放成熟Bean
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,为了将成熟bean和纯净bean分离开,避免读取到不完整的bean。
private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存,为了解决代码耦合
// 假设Student使用了AOP,@PointCut(""),要给Student创建动态代理
private static final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 当前创建的BeanName
private static final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 加载Bean定义,Spring里面将类加载成BeanDefinition,并存在Map中
*/
static void loadBeanDefinations() {
RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
beanDefinitionMap.put("student", studentDefinition);
beanDefinitionMap.put("teacher", teacherDefinition);
}
/**
* 获取Bean,先看看单例池有没有,单例池没有,看看二级缓存有没有,如果有的话去二级缓存拿
* 一级缓存存成熟bean,二级缓存存纯净bean。
* 如果A在创建依赖B的时候发现二级缓存有,去拿二级缓存中的B给自己注入,注入之后A就成熟了。
* 此时再循环到B的时候,注入A即可,A是成熟的了,完美解决循环依赖
* <p>
* AOP动态代理咋解决
* 直接在实例化之后,调用Bean的后置处理器,去解析Pointcut,然后创建动态代理即可。
* <p>
* Spring还是希望正常的Bean是在初始化之后创建动态代理,只有在循环依赖的前提下才在实例化之后创建
* 要判断是否是循环依赖。二级缓存如果有的话,就代表当前处于循环依赖状态。
* 但是如果二级缓存有的话,getBean函数就返回了,所以直接将创建动态代理的代码放在getSigton函数里面。
*
* @param beanName
*/
private static Object getBean(String beanName) throws Exception {
// 0、看看单例池有没有
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
if (!singletonsCurrentlyInCreation.contains(beanName)) {
// 将当前Bean设置为正在创建。
singletonsCurrentlyInCreation.add(beanName);
}
// 1、实例化
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
// 循环代理的Bean才会在实例化这里创建动态代理
singletonFactories.put(beanName, () -> {
// 动态代理根据原来的bean创建的,也就是原bean的信息还保留着。所以下面可以直接用二级缓存中的动态代理替换实例。
Object earlyBeanReference = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
return earlyBeanReference;
});
// 二级缓存不能写在这里了。因为创建代理是在三级缓存进行的,异步处理的。
// 二级缓存应该存的是AOP对象
// 假设A使用AOP,A在解析的时候去创建B
// B创建的时候,实例化A的动态代理对象,将动态代理对象注入到B
// A递归回来之后继续创建,此时A还是纯净对象,所以应该去二级缓存拿代理对象
earlySingletonObjects.put(beanName, beanInstance);
// 2、属性赋值
// 2.1、拿到所有的带有Autowerid注解的属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 拿到该字段的Autowired注解
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 拿到的注解不为空,就代表有该注解
if (annotation != null) {
// 打开该属性的访问权限,因为访问权限是private
declaredField.setAccessible(true);
// 递归调用getBean去获取属性teatcher
// Spring 可以根据Name、Type、构造函数来获取,这里只写name
String name = declaredField.getName();
Object bean = getBean(name);
declaredField.set(beanInstance, bean);
}
}
// 3、初始化,看看Bean有没有实现InitializingBean接口或者有没有指定init-method。
// 初始化之后,Spring也进行了动态代理的创建
// 4、添加到一级缓存
// 如果二级缓存有的话,就代表当前Bean已经生成动态代理了,就将当前实例赋值为二级缓存中的动态代理。
if (earlySingletonObjects.containsKey(beanName)) {
beanInstance = earlySingletonObjects.get(beanName);
}
singletonObjects.put(beanName, beanInstance);
// 后面还要remove掉二级缓存和三级缓存,根据当前Bean是否最后一次创建啥啥的。
return beanInstance;
}
// Spring为了解耦,将后置处理器代码写在getSingleton,获取单例函数里面代码不耦合。
private static Object getSingleton(String beanName) {
Object bean = singletonObjects.get(beanName);
// 一级缓存中没有,但是当前bean标识着正在创建,就代表着二三级缓存中有
if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
// 二级缓存中存的是代理对象,先从二级缓存拿
bean = earlySingletonObjects.get(beanName);
if (bean == null) {
// 一二级缓存都没有的话,去三级缓存拿
ObjectFactory<?> objectFactory = singletonFactories.get(beanName);
// 三级缓存的对象工厂调用getObject函数获取bean
if (objectFactory != null) {
bean = objectFactory.getObject();
// 存到二级缓存,以防止好多个相互依赖,有多个Bean依赖A
// 那么A就只用创建一次,然后放在二级缓存即可,下次直接从二级缓存中拿即可。
earlySingletonObjects.put(beanName, bean);
// 动态代理创建完之后就可以将当前beanName删除三级缓存
singletonFactories.remove(beanName);
}
}
}
return bean;
}
public static void main(String[] args) throws Exception {
loadBeanDefinations();
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
Student student = (Student) singletonObjects.get("student");
System.out.println(student.toString());
}
}
Spring的实现其实和这个例子基本是一致的。