模拟spring容器创建Bean并解决循环依赖 源码参考:https://gitee.com/itmc/spring-m gitee:https://gitee.com/itmc/spring-m.git
1.Spring创建Bean的过程
- 扫描配置获得BeanDefinitionMap
集Bean定义,像Bean类型,是否是单例,是否是懒加载等. - 遍历BeanDefinitionMap去创建Bean
- 构造推断
- 属性setter赋值
- 回调各种Aware,如BeanNameAware
- Aware回调后,判断是否实现BeanPostProcessor ,调用初始化前方法postProcessBeforeInitialization(),是否被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法
- 初始化如果当前instance是InitializingBean子类,则调用初始化方法
- 判断是否实现BeanPostProcessor ,调用初始化后方法postProcessAfterInitialization(),aop在这里实现
- 创建bean,Bean对象创建出来后:
2.1 Spring如何管理Bean
public class TestDemo {public static void main(String[] args) {ApplicationContext context=new AnnotationConfigApplicationContext("com.itmck.beans");UserServiceImpl bean = context.getBean("userServiceImpl",UserServiceImpl.class);bean.sayHi();}}
通过AnnotationConfigApplicationContext构造函数可知,我们要自定义spring的ioc容器,首先需要如下:
- 扫描配置生成BeanDefinitionMap
遍历BeanDefinitionMap创建Bean实例
public class AnnotationConfigApplicationContext {public AnnotationConfigApplicationContext(Class<?>... componentClasses) {this();register(componentClasses);refresh();}public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();}}
2.2模拟spring
2.2.1 创建容器
我们创建了AnnotationConfigApplicationContext并且拥有一个扫描方法,一个刷新方法(创建Bean方法),还有getBean()方法.简单架子如下所示:
说明:
- AnnotationConfigApplicationContext(Class<?> myConfigClass) 通过构造方法传入配置类
- scan(myConfigClass) 扫描获取Bean定义
- refresh() 创建Bean
- getBean(String beanName) 获取Bean ```java package com.itmck.spring.core;
/**
- 太阳当空照,花儿对我笑
Create by M ChangKe 2021/11/25 10:17 **/ public class AnnotationConfigApplicationContext {
/**
- 通过构造方法传入配置类 *
@param myConfigClass 配置类 */ public AnnotationConfigApplicationContext(Class<?> myConfigClass) {
scan(myConfigClass); refresh(); }
/**
- 扫描配置类 *
@param myConfigClass 配置类 */ private void scan(Class<?> myConfigClass) { }
/**
创建Bean */ private void refresh() {
}
/*** 通过BeanName获取Bean对象** @param beanName Bean别名* @return Bean对象*/public Object getBean(String beanName) {return null;}/*** 通过通过BeanName一级Bean class获取Bean对象** @param beanName Bean别名* @param tClass bean class类型* @param <T> 实例泛型* @return Bean对象*/public <T> T getBean(String beanName, Class<T> tClass) {return tClass.cast(getBean(beanName));}
}
<a name="YWJAA"></a>### 2.2.2 定义注解创建注解用于标记bean- @Component```javapackage com.itmck.spring.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Component {String value() default "";}
- @Autowired ```java package com.itmck.spring.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Autowired {
}
- @ComponentScan```javapackage com.itmck.spring.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** 类扫描注解** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/9/11 16:44**/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ComponentScan {String value() default "";}
- @Scope ```java package com.itmck.spring.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Scope {
String value() default "";
}
<a name="D6GNa"></a>### 2.2.3 创建BeanDefinition扫描的目的就是扫描出BeanDefinition集合.BeanDefinition中包含了Bean类型,是否单例,是否懒加载....等```javapackage com.itmck.spring.core;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/9/11 17:28**/public class BeanDefinition {/*** bean类型*/private Class<?> type;/*** 是否是单例*/private String scope;/*** 是否是懒加载*/private boolean isLazy;//省略getter/setter...}
2.2.4 创建scan(),refresh()以及getBean()方法
package com.itmck.spring.core;import com.itmck.spring.annotation.Component;import com.itmck.spring.annotation.ComponentScan;import com.itmck.spring.annotation.Scope;import java.beans.Introspector;import java.io.File;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.net.URL;import java.util.HashMap;import java.util.Map;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/11/25 10:17**/public class AnnotationConfigApplicationContext {/*** beanDefinitionMap 存放多个Bean定义 其中key为BeanName,value为BeanDefinition*/private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();/*** 单例池,如果创建的是单例对象则放入单例池中*/private final Map<String, Object> singletonObjects = new HashMap<>(256);/*** 通过构造方法传入配置类** @param myConfigClass 配置类*/public AnnotationConfigApplicationContext(Class<?> myConfigClass) {scan(myConfigClass);refresh();}/*** 扫描配置类** @param myConfigClass 配置类*/private void scan(Class<?> myConfigClass) {if (myConfigClass.isAnnotationPresent(ComponentScan.class)) {//如果当前配置类上存在注解@ComponentScan,则代表当前类为配置类ComponentScan componentScan = myConfigClass.getAnnotation(ComponentScan.class);//获取value内容 com.itmck.mckString path = componentScan.value();path = path.replace(".", "/");//将com.itmck.mck-->com/itmck/mck//获取当前类加载器ClassLoader classLoader = this.getClass().getClassLoader();URL resource = classLoader.getResource(path);assert resource != null;File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();assert files != null;for (File f : files) {String absolutePath = f.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");Class<?> aClass = null;try {//通过类加载器,加载当前类信息aClass = classLoader.loadClass(absolutePath);} catch (ClassNotFoundException e) {e.printStackTrace();}assert aClass != null;if (aClass.isAnnotationPresent(Component.class)) {//如果当前类上存在存在@Component注解Component component = aClass.getAnnotation(Component.class);String beanName = component.value();if ("".equals(beanName)) {//如果没有给默认值,则使用类的首字母小写beanName = Introspector.decapitalize(aClass.getSimpleName());}//扫描的目的就是获取BeanDefinitionBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(aClass);//判断是否存在Scope注解标记if (aClass.isAnnotationPresent(Scope.class)) {Scope scope = aClass.getAnnotation(Scope.class);String value = scope.value();beanDefinition.setScope(value);} else {beanDefinition.setScope("singleton");}beanDefinitionMap.put(beanName, beanDefinition);}}}}}/*** 创建Bean*/private void refresh() {//在这里拿到beanDefinitionMap后即可对Bean进行创建beanDefinitionMap.forEach((beanName, beanDefinition) -> {if ("singleton".equals(beanDefinition.getScope())) {try {Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);} catch (Exception e) {e.printStackTrace();}}});}private Object createBean(String beanName, BeanDefinition beanDefinition) {Class<?> aClass = beanDefinition.getType();Object instance = null;if (aClass.isAnnotationPresent(Component.class)) {try {instance = aClass.getConstructor().newInstance();//无参构造创建BeanField[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields) {String name = field.getName();field.setAccessible(true);field.set(instance, getBean(name));}} catch (Exception e) {e.printStackTrace();}}return instance;}/*** 通过BeanName获取Bean对象** @param beanName Bean别名* @return Bean对象*/public Object getBean(String beanName) {if (!beanDefinitionMap.containsKey(beanName)) {throw new NullPointerException();}BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//如果是获取的Bean在单例池中存在则从单例池中取,否则新建if ("singleton".equals(beanDefinition.getScope())) {Object singletonBean = singletonObjects.get(beanName);if (singletonBean == null) {singletonBean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, singletonBean);}return singletonBean;} else {// 原型Beanreturn createBean(beanName, beanDefinition);}}/*** 通过通过BeanName一级Bean class获取Bean对象** @param beanName Bean别名* @param tClass bean class类型* @param <T> 实例泛型* @return Bean对象*/public <T> T getBean(String beanName, Class<T> tClass) {return tClass.cast(getBean(beanName));}}
到此位置,已经成功创建spring-m并初始化了相关方法.下一步就是应用案例测试
2.2.5 框架测试
2.2.5.1 创建 OneConfig.class
package com.itmck.one;import com.itmck.spring.annotation.ComponentScan;@ComponentScan("com.itmck.one")public class OneConfig {}
2.2.5.2 创建User对象
package com.itmck.one;import com.itmck.spring.annotation.Autowired;import com.itmck.spring.annotation.Component;@Componentpublic class User {public void run() {System.out.println("User#run() is running...");}}
2.2.5.3 测试
package com.itmck.one;import com.itmck.mck.MyConfig;import com.itmck.mck.UserA;import com.itmck.mck.UserB;import com.itmck.spring.core.AnnotationConfigApplicationContext;import com.itmck.spring.core.AnnotationConfigApplicationContext2;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/11/24 9:36**/public class TestDemo2 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);User user = applicationContext.getBean("user", User.class);user.run();}}
运行结果 User#run() is running…
注:上述代码是针对单个Bean进行扫描以及创建的简单流程梳理,但spring中远不止这些.
循环依赖
什么是循环依赖?
- 循环依赖就是A依赖B,B依赖A,互相依赖.主要分为一个类之间,两个类,多个类之间的依赖

- 循环依赖分为构造器依赖和属性依赖,众所周知的是Spring能够解决属性的循环依赖(set注入)
如何解决循环依赖?
简单来说,解决循环依赖就是加一个出口,方式死循环.
怎样增加出口?简单说就是增加一个缓存
模拟spring解决循环依赖
spring中使用三级缓存解决循环依赖
- singletonObjects 单例池
- earlySingletonObjects 纯净bean(刚实例化的Bean)
- singletonFactories 存放用于创建Bean的工厂 ObjectFactory
模拟解决循环依赖
- UserA ```java package com.itmck.mck;
import com.itmck.spring.annotation.Autowired; import com.itmck.spring.annotation.Component;
/**
- 太阳当空照,花儿对我笑
Create by M ChangKe 2021/9/11 18:08 **/ @Component public class UserA {
@Autowired private UserB userB;
public void run() {
System.out.println("UserA");
} //省略get/set } ```
- UserB ```java package com.itmck.mck;
import com.itmck.spring.annotation.Autowired; import com.itmck.spring.annotation.Component;
/**
- 太阳当空照,花儿对我笑
Create by M ChangKe 2021/9/11 18:08 **/ @Component public class UserB {
@Autowired private UserA userA;
public void run(){
System.out.println("UserB");
} //略get/set
}
<a name="e6Zj3"></a>### 一级缓存<a name="omP4n"></a>#### 一级缓存如何解决?首先引入 ** private final Map<String, Object> singletonObjects = new HashMap<>(256); 作为一级缓存**<br />修改createBean()方法是关键.因为A依赖B,在创建A的时候给A属性赋值,这时需要创建B.然后创建B又依赖到A....循环往复.怎么办????- **调用createBean()进行递归创建**- **防止递归循环需要添加一个出口函数**```javaprivate Object createBean(String beanName, BeanDefinition beanDefinition) {//使用一级缓存解决循环依赖//1================在这里先判断一级缓存中是否存在,这里是解决循环依赖的关键,添加一个递归出口,防止无限递归====Object singleton = getSingleton(beanName);if (singleton != null) {return singleton;}//================在这里先判断一级缓存中是否存在,这里是解决循环依赖的关键,添加一个递归出口,防止无限递归=====Class<?> aClass = beanDefinition.getType();Object instance = null;if (aClass.isAnnotationPresent(Component.class)) {try {instance = aClass.getConstructor().newInstance();//无参构造创建Bean//2==================将instance放入一级缓存======================singletonObjects.put(beanName,instance);//将instance放入一级缓存//==================将instance放入一级缓存======================Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields) {field.setAccessible(true);//**在这里调用自身方法进行递归操作获取bean**field.set(instance,createBean(field.getName(), beanDefinitionMap.get(field.getName())));}} catch (Exception e) {e.printStackTrace();}}return instance;}public Object getSingleton(String beanName) {if (singletonObjects.containsKey(beanName)) {return singletonObjects.get(beanName);}return null;}
测试
package com.itmck;import com.itmck.mck.MyConfig;import com.itmck.mck.UserA;import com.itmck.mck.UserB;import com.itmck.one.OneConfig;import com.itmck.one.User;import com.itmck.spring.core.AnnotationConfigApplicationContext;import com.itmck.spring.core.AnnotationConfigApplicationContext2;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/11/24 9:36**/public class MckSpringApplication {public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext2 applicationContext = new AnnotationConfigApplicationContext2(MyConfig.class);UserA userA = applicationContext.getBean("userA", UserA.class);UserB userB1 = userA.getUserB();System.out.println("UserA#userB.run()");userB1.run();UserB userB = applicationContext.getBean("userB", UserB.class);UserA userA1 = userB.getUserA();System.out.println("UserB#userA.run()");userA1.run();}}
结果如下,表示循环依赖已经解决
UserA#userB.run()UserBUserB#userA.run()UserA
现在看上去一级缓存已经能够解决循环依赖问题.
如果是多线程环境下
假如现有2个线程:线程A在创建BeanA的同时线程B创建BeanB
A线程在创建beanA的时候,实例化后加入一级缓存,B线程在创建B的时候依赖A此时去一级缓存中getBeanA,拿到的是不完整的beanA.
所以一级缓存解决的是单线程环境下的循环依赖,多线程环境仍然不能保证解决.此时需要引入二级缓存
二级缓存
二级缓存如何解决?
修改代码如下,新增二级缓存earlySingletonObjects用于存放纯净Bean
private final Map
earlySingletonObjects = new ConcurrentHashMap<>(256); 改造scan()与getSingleton()方法 ```java private Object createBean(String beanName, BeanDefinition beanDefinition) { //使用一级缓存解决循环依赖 //在这里先判断一级缓存中是否存在,这里是解决循环依赖的关键,添加一个递归出口,防止无限递归 Object singleton = getSingleton(beanName); if (singleton != null) {
return singleton;
} Class<?> aClass = beanDefinition.getType(); Object instance = null; if (aClass.isAnnotationPresent(Component.class)) {
try {instance = aClass.getConstructor().newInstance();//无参构造创建BeanearlySingletonObjects.put(beanName, instance);//这里新增二级缓存存放新鲜反射出来的纯净BeanField[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields) {field.setAccessible(true);field.set(instance, createBean(field.getName(), beanDefinitionMap.get(field.getName())));//在这里调用自身方法进行递归操作获取bean}} catch (Exception e) {e.printStackTrace();}
} //初始化在这里 //init——— singletonObjects.put(beanName, instance);//将instance放入一级缓存,这里的Bean是完成了属性赋值,初始化等完整的单例Bean return instance; }
/**
- 通过判断 *
- @param beanName Bean别名
- @return Bean对象
*/
public Object getSingleton(String beanName) {
if (singletonObjects.containsKey(beanName)) {
}else if (earlySingletonObjects.containsKey(beanName)){return singletonObjects.get(beanName);
} return null;//新增二级缓存后,一级缓存没有从二级缓存中取return earlySingletonObjects.get(beanName);
}
上述代码注意:注意:earlySingletonObjects.put(beanName, instance)与singletonObjects.put(beanName, instance)位置<br />最后运行结果与上述一致<a name="I2Okh"></a>### 总结:一级缓存和二级缓存的作用- 一级缓存: 解决循环依赖的问题,但是不能完全解决,因为存在多线程问题- 二级缓存: 在创建实例bean和放入到一级缓存之间还有一段间隙. 如果在这之间从一级缓存拿实例, 肯定是返回null的. 为了避免这个问题, 增加了二级缓存.(多线程问题)<a name="Ca19M"></a>## 到这里可以发现使用二级缓存就可以解决循环依赖中Aop的问题.那么为什Spring使用了三级缓存?<a name="DsJGU"></a>## <br />aop创建时机**我们在创建bean 的时候, 会有很多Bean的后置处理器BeanPostProcessor. 如果有AOP, 会在什么时候创建呢? 在初始化以后, 调用BeanPostProcessor创建动态代理?**结合上面的代码, 我们想一想, 其实在初始化以后创建动态代理就晚了. 为什么呢? 因为, 如果有循环依赖, 在初始化之后才调用, 那就不是动态代理. 其实我们这时候应该在实例化之后, 放入到二级缓存之前调用. 如果判断有循环依赖此时进行AOP,那么最佳位置应该是在判断二级缓存存在说明是循环依赖,此时进行aop然后将新的实例放入二级缓存修改后代码如下:```javapublic Object getSingleton(String beanName) {if (singletonObjects.containsKey(beanName)) {return singletonObjects.get(beanName);}else if (earlySingletonObjects.containsKey(beanName)){//新增二级缓存后,一级缓存没有从二级缓存中取/*** 第一次创建bean是正常的instanceBean. 他并不是循环依赖. 第二次进来判断, 这个bean已经存在了, 就说明是循环依赖了* 这时候通过动态代理创建bean. 然后将这个bean在放入到二级缓存中覆盖原来的instanceBean.* 这样我们在循环依赖的时候就完成了AOP的创建. 这是在二级缓存里创建的AOP*/Object instance = new CglibProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);earlySingletonObjects.put(beanName, instance);return earlySingletonObjects.get(beanName);}return null;}
创建CglibProxyBeanPostProcessor
package com.itmck.mck;import com.itmck.spring.annotation.Component;import com.itmck.spring.core.SmartInstantiationAwareBeanPostProcessor;import com.itmck.spring.proxy.MyEnhancer;import com.itmck.spring.proxy.MyProxy;import net.sf.cglib.proxy.Enhancer;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/9/11 18:08**/@Componentpublic class CglibProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {// 假设A被切点命中 需要创建代理 @PointCut("execution(* *..InstanceA.*(..))")/*** 这里, 我们简单直接判断bean是不是InstanceA实例, 如果是, 就创建动态代理.* 这里没有去解析切点, 解析切点是AspectJ做的事.*/if (bean instanceof UserA) {MyEnhancer myEnhancer = new MyEnhancer(new MyProxy(),bean);Enhancer enhancer = myEnhancer.getEnhancer();bean = enhancer.create();}return bean;}}
MyEnhancer
使用cglib代理需要引入pom
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>
package com.itmck.spring.proxy;import net.sf.cglib.proxy.Enhancer;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/11/25 15:11**/public class MyEnhancer {private final MyProxy myProxy;private final Object object;public MyEnhancer(MyProxy myProxy, Object object) {this.myProxy = myProxy;this.object = object;}public Enhancer getEnhancer(){Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程enhancer.setSuperclass(object.getClass()); // 设置enhancer对象的父类enhancer.setCallback(myProxy); // 设置enhancer的回调对象return enhancer;}}
MyProxy implements MethodInterceptor
package com.itmck.spring.proxy;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MyProxy implements MethodInterceptor {/**** @param o cglib生成的代理对象* @param method 被代理对象的方法* @param objects 传入方法的参数* @param methodProxy 代理的方法* @return* @throws Throwable*/public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("这里可以插入执行关键代码之前的逻辑");Object o1 = methodProxy.invokeSuper(o, objects);//关键代码:System.out.println("这里可以插入执行关键代码之后的逻辑");return o1;}}
此时运行代码结果
UserA#userB.run()UserBUserB#userA.run()这里可以插入执行关键代码之前的逻辑UserA这里可以插入执行关键代码之后的逻辑
通过上面发现:如果在getBean的时候写,不符合单一职责标准。后置处理器是在bean的创建过程中去给它增强的.所以三级缓存是为了单一职责
引入三级缓存
如何引入?
- public final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
在createBean中实例化之后进行保存ObjectFactory 到三级缓存singletonFactories ```java private Object createBean(String beanName, BeanDefinition beanDefinition) { //使用一级缓存解决循环依赖 //在这里先判断一级缓存中是否存在,这里是解决循环依赖的关键,添加一个递归出口,防止无限递归 Object singleton = getSingleton(beanName); if (singleton != null) {
return singleton;
} // 正在创建 singletonsCurrentlyInCreation.add(beanName);
Class<?> aClass = beanDefinition.getType(); Object instance = null; if (aClass.isAnnotationPresent(Component.class)) {
try {instance = aClass.getConstructor().newInstance();//无参构造创建Bean//在这里直接进行aop不合理,因为没有进行判断,所以要先判断是否需要进行代理,如果需要才进行aop,在哪里判断?//当有循环依赖的时候,二级缓存不为null,所以在getSingleton()中判断//instance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instance, beanName);//实例化之后保 级缓存就是存函数接口的singletonFactories.put(beanName,() -> new CglibProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName));earlySingletonObjects.put(beanName, instance);//这里新增二级缓存存放新鲜反射出来的纯净BeanField[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields) {field.setAccessible(true);field.set(instance, createBean(field.getName(), beanDefinitionMap.get(field.getName())));//在这里调用自身方法进行递归操作获取bean}} catch (Exception e) {e.printStackTrace();}
} /**
- 第四步: 初始化
初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了 */
//正常的代理实在初始化之后,就是这里进行,但是如果存在循环依赖必须在实例化之后进行 //instance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instance, beanName);
// 由于递归完后A 还是原实例,, 所以要从二级缓存中拿到proxy 。if (earlySingletonObjects.containsKey(beanName)) {instance = earlySingletonObjects.get(beanName);}//将instance放入一级缓存,这里的Bean是完成了属性赋值,初始化等完整的单例BeansingletonObjects.put(beanName, instance);return instance;
}
<a name="ElS8z"></a>### <br />getSingleton()修改如下```javapublic Object getSingleton(String beanName) {// 先从一级缓存中拿Object bean = singletonObjects.get(beanName);// 说明是循环依赖if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {bean = earlySingletonObjects.get(beanName);// 如果二级缓存没有就从三级缓存中拿if (bean == null) {// 从三级缓存中拿ObjectFactory<?> factory = singletonFactories.get(beanName);if (factory != null) {try {bean = factory.getObject(); // 拿到动态代理earlySingletonObjects.put(beanName, bean);//然后从二级缓存移除singletonFactorythis.singletonFactories.remove(beanName);} catch (Exception e) {e.printStackTrace();}}}}return bean;}
完整代码
package com.itmck.spring.core;import com.itmck.mck.CglibProxyBeanPostProcessor;import com.itmck.spring.annotation.Component;import com.itmck.spring.annotation.ComponentScan;import com.itmck.spring.annotation.Scope;import java.beans.Introspector;import java.io.File;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/11/25 10:17** 模拟spring读取配置文件以及创建Bean,引入三级缓存目的,存放ObjectFactory,单一自责***/public class AnnotationConfigApplicationContext3 {/*** beanDefinitionMap 存放多个Bean定义 其中key为BeanName,value为BeanDefinition*/private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();/*** 单例池,如果创建的是单例对象则放入单例池中*/private final Map<String, Object> singletonObjects = new HashMap<>(256);//早期Bean,还未被初始化以及属性赋值private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存public final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();//循环依赖标识public final Set<String> singletonsCurrentlyInCreation = new HashSet<>();/*** 通过构造方法传入配置类** @param myConfigClass 配置类*/public AnnotationConfigApplicationContext3(Class<?> myConfigClass) {scan(myConfigClass);refresh();}/*** 扫描配置类** @param myConfigClass 配置类*/private void scan(Class<?> myConfigClass) {if (myConfigClass.isAnnotationPresent(ComponentScan.class)) {//如果当前配置类上存在注解@ComponentScan,则代表当前类为配置类ComponentScan componentScan = myConfigClass.getAnnotation(ComponentScan.class);//获取value内容 com.itmck.mckString path = componentScan.value();path = path.replace(".", "/");//将com.itmck.mck-->com/itmck/mck//获取当前类加载器ClassLoader classLoader = this.getClass().getClassLoader();URL resource = classLoader.getResource(path);assert resource != null;File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();assert files != null;for (File f : files) {String absolutePath = f.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");Class<?> aClass = null;try {//通过类加载器,加载当前类信息aClass = classLoader.loadClass(absolutePath);} catch (ClassNotFoundException e) {e.printStackTrace();}assert aClass != null;if (aClass.isAnnotationPresent(Component.class)) {//如果当前类上存在存在@Component注解Component component = aClass.getAnnotation(Component.class);String beanName = component.value();if ("".equals(beanName)) {//如果没有给默认值,则使用类的首字母小写beanName = Introspector.decapitalize(aClass.getSimpleName());}//扫描的目的就是获取BeanDefinitionBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(aClass);//判断是否存在Scope注解标记if (aClass.isAnnotationPresent(Scope.class)) {Scope scope = aClass.getAnnotation(Scope.class);String value = scope.value();beanDefinition.setScope(value);} else {beanDefinition.setScope("singleton");}beanDefinitionMap.put(beanName, beanDefinition);}}}}}/*** 创建Bean*/private void refresh() {//在这里拿到beanDefinitionMap后即可对Bean进行创建beanDefinitionMap.forEach((beanName, beanDefinition) -> {if ("singleton".equals(beanDefinition.getScope())) {try {createBean(beanName, beanDefinition);} catch (Exception e) {e.printStackTrace();}}});}private Object createBean(String beanName, BeanDefinition beanDefinition) {//使用一级缓存解决循环依赖//在这里先判断一级缓存中是否存在,这里是解决循环依赖的关键,添加一个递归出口,防止无限递归Object singleton = getSingleton(beanName);if (singleton != null) {return singleton;}// 正在创建singletonsCurrentlyInCreation.add(beanName);Class<?> aClass = beanDefinition.getType();Object instance = null;if (aClass.isAnnotationPresent(Component.class)) {try {instance = aClass.getConstructor().newInstance();//无参构造创建BeanObject finalInstance = instance;singletonFactories.put(beanName, (ObjectFactory<?>)()-> new CglibProxyBeanPostProcessor().getEarlyBeanReference(finalInstance, beanName));earlySingletonObjects.put(beanName, instance);//这里新增二级缓存存放新鲜反射出来的纯净BeanField[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields) {field.setAccessible(true);field.set(instance, createBean(field.getName(), beanDefinitionMap.get(field.getName())));//在这里调用自身方法进行递归操作获取bean}} catch (Exception e) {e.printStackTrace();}}/*** 第四步: 初始化* 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了*/// 由于递归完后A 还是原实例,, 所以要从二级缓存中拿到proxy 。if (earlySingletonObjects.containsKey(beanName)) {instance = earlySingletonObjects.get(beanName);}//将instance放入一级缓存,这里的Bean是完成了属性赋值,初始化等完整的单例BeansingletonObjects.put(beanName, instance);return instance;}public Object getSingleton(String beanName) {// 先从一级缓存中拿Object bean = singletonObjects.get(beanName);// 说明是循环依赖if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {bean = earlySingletonObjects.get(beanName);// 如果二级缓存没有就从三级缓存中拿if (bean == null) {// 从三级缓存中拿ObjectFactory<?> factory = singletonFactories.get(beanName);if (factory != null) {try {bean = factory.getObject(); // 拿到动态代理earlySingletonObjects.put(beanName, bean);//然后从二级缓存移除singletonFactorythis.singletonFactories.remove(beanName);} catch (Exception e) {e.printStackTrace();}}}}return bean;}public Object getBean(String beanName) {if (!beanDefinitionMap.containsKey(beanName)) {throw new NullPointerException();}BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//如果是获取的Bean在单例池中存在则从单例池中取,否则新建if ("singleton".equals(beanDefinition.getScope())) {Object singletonBean = singletonObjects.get(beanName);if (singletonBean == null) {singletonBean = createBean(beanName, beanDefinition);}return singletonBean;} else {// 原型Beanreturn createBean(beanName, beanDefinition);}}/*** 通过通过BeanName一级Bean class获取Bean对象** @param beanName Bean别名* @param tClass bean class类型* @param <T> 实例泛型* @return Bean对象*/public <T> T getBean(String beanName, Class<T> tClass) {return tClass.cast(getBean(beanName));}}
运行结果与上述一致
总结
spring使用三级缓存解决循环依赖
- 一级缓存能解决循环依赖,但是不能解决多线程环境下循环依赖
- 二级缓存用于存放纯净Bean(实例化之后还未赋值以及初始化的Bean),并且能解决多线程下循环依赖.其次二级缓存也能解决aop
- 三级缓存则是存放ObjectFactory用于创建aop之久的代理Bean.spring也是考虑职责单一
这样以来.职责明确.一级缓存单例池,二级缓存纯净Bean,三级缓存存放用于创建代理Bean的ObjectFactory.
在创建bean的时候, 在哪里创建的动态代理, 这个应该怎么回答呢?
很多人会说在初始化之后, 或者在实例化之后. 其实更严谨的说, 有两种情况: 第一种是在初始化之后调用 . 第二种是出现了循环依赖, 会在实例化之后调用
注:本过程简单模拟SpringBean的创建过程.这里只是简单模拟.具体Spring的流程要比这多