3.Java系列之Spring面试题总结

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的生命周期

  1. Spring容器从XML文件中读取bean的定义,并实例化bean
  2. Spring根据bean的定义填充所有属性
  3. 如果bean实现了BeanNameAware接口,Spring传递bean的Id到setBeanName方法
  4. 如果bean实现了BeanFactoryAware接口, Spring传递beanFactory到setBeanFactory方法
  5. 如果bean实现了BeanPostProcessor接口,postProcessorBeforeInitialization方法将被调用
  6. 如果bean实现了InitializingBean接口,afterPropertiesSet方法被调用;如果bean声明了初始化方法,则调用该初始化方法
  7. 如果bean实现了BeanPostProcessor接口,postProcessorAfterInitialization方法将被调用
  8. 如果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();
}

欢迎关注公众号算法小生,本文持续更新中

猜你喜欢

转载自blog.csdn.net/SJshenjian/article/details/130175790