【Java Spring基本问题】记录面试题宝典中自己不熟悉的Spring问题
文章目录
- Spring Bean定义装配
- Spring Bean生命周期
- Spring Bean容器
- Spring 循环依赖
- Spring 事务
- @Autowired和@Resource
Spring Bean定义装配
参考文章
1. 定义Spring Bean的三种方式
XML文件定义Spring Bean
JavaConfig定义Spring Bean
@Component注解定义SpringBean
2. 装配Spring Bean的四种方式
手动装配 + XML文件
自动装配 + XML文件
手动装配 + JavaConfig文件
自动装配 + 注解
Spring Bean生命周期
参考文章:Bean的生命周期(不要背了记思想)
参考文章:Bean流程非常详细
Bean注册
调用各种BeanDefinitionReader读取各种配置来源信息,并将其转化为BeanDefintion的过程。
Bean实例化
调用Spring Bean的构造器实例化
Bean属性注入
调用postProcessPropertyValues方法,对Spring Bean属性注入
调用一系列Aware接口的实现类,获取Bean的属性(BeanName、BeanFactory等等)
Bean初始化
调用的init-method属性指定的初始化方法
调用初始化前方法postProcessBeforeInitialization
调用InitializingBean.afterPropertiesSet属性设置后方法
调用初始化后方法postProcessAfterInitialization
Bean销毁
调用DiposibleBean.destory()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean. 实例化bean
BeanWrapper instanceWrapper = null;
.....
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
.....
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//添加到三级缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充属性
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
......
return exposedObject;
}
Spring Bean容器
Spring主要提供了两种类型的容器:BeanFactory和ApplicationContext
1. BeanFactory
使用示例
XmlBeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
2. ApplicationContext
使用示例
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
3. 两者区别
1. ApplicationContext,继承于BeanFactory,是相对比较高级的容器实现。
2. BeanFactory在启动的时候不会去实例化Bean,ApplicationContext在启动的时候就把所有的Bean全部实例化了
Spring 循环依赖
1. Spring循环依赖的三种情况
构造器的循环依赖
:无法处理
非单例Bean循环依赖
:无法处理
单例Bean循环依赖
:使用三级缓存处理
2. 单例Bean循环依赖的解决
使用三级缓存
// 完成初始化的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 完成实例化但是尚未填充属性和初始化的单例Bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 完成实例化但是尚未填充属性和初始化的单例Bean,并在这里发生AOP后置处理
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
三级缓存解决循环依赖
- Spring 创建 bean 主要分为两个步骤,创建原始 bean 对象,接着去填充对象属性和初始化
- 每次创建 bean 之前,我们都会从缓存中查下有没有该 bean,因为是单例,只能有一个
- 当我们创建 beanA 的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了 beanB,接着就又去创建 beanB,同样的流程,创建完 beanB 填充属性时又发现它依赖了 beanA,又是同样的流程,不同的是,这时候可以在三级缓存中查到刚放进去的原始对象 beanA,所以不需要继续创建,用它注入 beanB,完成 beanB 的创建
- 既然 beanB 创建好了,所以 beanA 就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成
为什么需要三级缓存呢,二级他也够了呀
三级缓存就是为了后置处理,也就是说你不需要后置处理的话,那就不需要三级缓存,直接二级缓存就可以解决循环依赖。
那为什么二级缓存中不可以进行后置处理
假设我们现在是二级缓存架构,创建 A 的时候,我们不知道有没有循环依赖,所以放入二级缓存提前暴露,接着创建 B,也是放入二级缓存,这时候发现又循环依赖了 A,就去二级缓存找,是有,但是如果此时还有 AOP 代理呢,我们要的是代理对象可不是原始对象,这怎么办,只能改逻辑,在第一步的时候,不管3721,所有 Bean 统统去完成 AOP 代理,如果是这样的话,就不需要三级缓存了,但是这样不仅没有必要,而且违背了 Spring 在结合 AOP 跟 Bean 的生命周期的设计。
3. 无法解决循环依赖的场景
1. 构造器的循环依赖
将已经实例化但未完成属性填充的Bean放在三级缓存中,可以解决循环依赖的问题,但是如果是构造器的循环依赖,那么Bean连第一步的实例化都无法完成,即构造器无法执行。
2. 非单例Bean的循环依赖
因为 Spring 容器不进行缓存 prototype 作用域的 bean ,因此无法提前暴露一个创建中的bean 。
Spring 事务
1. 声明式事务和编程式事务
声明式事务
就是通过注解即配置的方式进行事务的管理
编程式事务
就是自己在业务逻辑种编写代码来管理事务
2. 7种事务传播行为
参考文章:事务传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Autowired和@Resource
1. @Autowired和@Qualifier
public class Pepole {
private String name;
//1. 通过名字装配
@Autowired
@Qualifier("books1")
private Books books;
//2. 通过类型装配
@Autowired
private Hobbies hobbies;
}
2. @Resource
// @Resource指定按type自动装配
@Resource(type = Books.class)
private Books books;
// @Resource指定按name自动装配
@Resource(name = books)
private Books books;