1. 如何解决循环引用
循环依赖就是N个类循环嵌套引用。
1.1 循环依赖发生的时机
Spring单例对象的初始化大略分为三步:
1.createBeanInstance: 实例化,其实就是调用对象的构造方法进行实例化对象
2.populateBean: 填充属性,这一步主要是多bean的依赖属性进行填充
3.initialzeBean: 初始化
循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖
1.2 构造器循环依赖
抛出BeanCurrentlylnCreationException异常
1.3 单例setter循环依赖
Spring为了解决单例的循环依赖问题,使用了三级缓存
先让最底层对象完成初始化,通过三级缓存与二级缓存提前暴露创建中的bean,让其他bean率先完成初始化
/** 一级缓存:完成初始化的单例对象的cache **/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** 二级缓存:完成实例化但尚未填充属性初始化的单例对象的cache */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** 三级缓存:进入实例化阶段单例对象工厂的cache */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/**
bean 的获取过程:先从一级获取,失败再从二级、三级里面获取
创建中状态:是指对象已经 new 出来了但是所有的属性均为 null 等待被 init
*/
检测循环依赖的过程如下:
- A创建过程中需要B,于是A将自己放入三级缓存里面,去实例化B
- B实例化的时候发现需要A,于是B先检查一级缓存,没有在查二级缓存,还是没有,则在查三级缓存,找到了!
- 然后把三级缓存里的A放入二级缓存,并删除三级缓存里的A
- B顺利初始化完毕,将自己放入一级缓存(此时B里面的A依然是创建中状态)
- 然后回来接着创建A,此时B已经创建结束,直接从一级缓存中拿到B,然后完成创建,并将自己放入一级缓存中
- 如此依赖便解决了循环依赖的问题
// 以上叙述的源码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
1.4 非单例循环依赖
Spring无法完成依赖注入。因为Spring不缓存“prototype”多例作用域的bean,因此无法提前暴露一个创建中的bean
2. 使用Spring框架的好处是?
轻量 Spring是轻量的,基本版本约为2M
控制反转(IOC) Spring通过控制反转实现松耦合,对象们给出了依赖,而不是创建与查找依赖的对象们
面向切面(AOP) Spring支持AOP编程,把业务逻辑与系统服务分开
容器 Spring管理应用中对象的生命周期与配置
MVC框架
事务管理 Spring提供事务管理接口,上至本地事务,下至全局事务
异常处理 Spring提供API将具体技术相关的异常转化为一致的unchecked异常
3. Spring由哪些模块组成
spring-core spring-beans spring-context spring-aop spring-expression
spring-jdbc spring-oxm spring-tx spring-web spring-webmvc
4. ApplicationContext通常的实现是什么?
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
WebXmlApplicationContext
5. 解释Spring支持的几种bean的作用域
singleton 容器中只有一个对应bean实例
prototype 一个bean的定义有多个实例
以下四种基于web模块下的webApplicationContext
request
session
globalSession
application
6. Spring框架中的单例bean是线程安全的吗
不是
7. 解释Spring框架中bean的生命周期
- Spring容器从XML文件中读取bean的定义,并实例化bean
- Spring根据bean的定义填充所有属性
- 如果bean实现了BeanNameAware接口,Spring传递bean的Id到setBeanName方法
- 如果bean实现了BeanFactoryAware接口, Spring传递beanFactory到setBeanFactory方法
- 如果bean实现了BeanPostProcessor接口,postProcessorBeforeInitialization方法将被调用
- 如果bean实现了InitializingBean接口,afterPropertiesSet方法被调用;如果bean声明了初始化方法,则调用该初始化方法
- 如果bean实现了BeanPostProcessor接口,postProcessorAfterInitialization方法将被调用
- 如果bean实现了DisposableBean接口,destroy方法将被调用
8. Spring团队为什么推荐使用构造器注入,而非变量注入
Java类会先执行构造方法,然后再给注解了@Autowired的对象注入值,如下会报错:
@Autowired
private User user;
private String school;
public UserAccountServiceImpl(){
this.school = user.getSchool();
}
改为推荐的注入方式即可:
private User user;
private String school;
@Autowired
public UserAccountServiceImpl(User user){
this.user = user;
this.school = user.getSchool();
}
欢迎关注公众号算法小生,本文持续更新中