大家好,我是了不起
,前段时间,了不起
在当面试官,挑了许多人给leader去面谈,最后可能是因为把之前某个想走的同事留了下来了,所以对新人没有太多的要求,所以选了应届生。
感觉如果是这种情况,还是比较利好应届生的,不然有些业务比较特殊的活,需要有能力接下上一任的工作,对面试的人要求会非常的高,人也不好找,最后头疼的也是我。
不提也罢,回归正题,分享一道最近常用来面试1-2年工作经验的人的面试题吧。
在软件开发的世界里,我们总是追求代码的优雅与高效。目前Java主流的SpringBoot、SpringCloud框架无疑是我们最好的帮手。它不仅简化了企业级应用的开发,还为我们提供了许多强大的功能。
比如依赖注入DI,但是,就像任何技术都有其双刃剑的一面,依赖注入也不例外,Spring在进行依赖注入时最常见的一个问题——循环依赖。
举例一个场景,我们有两个Service类A和B,A类里有个a2方法需要调用了B类里的b1方法,B类里的b2方法需要用到A类里的a1方法。
那么按照我们Java代码的无脑编程,就会是下面的这个情况:
ServiceA的a2方法调用ServiceB里的b1方法
@Service
public class ServiceA implements Service {
@Autowired
private ServiceB serviceB;
@Override
public void a1() {
System.out.println("当前是ServiceA的a1方法"");
}
@Override
public void a2() {
System.out.println("这里将调用ServiceB的b1方法");
serviceB.b1();
}
}
同样ServiceB的b2方法就调用ServiceA里的a1方法
@Service
public class ServiceB implements Service{
@Autowired
private ServiceA serviceA;
@Override
public void b1() {
System.out.println("当前是ServiceB的b1方法");
}
@Override
public void b2() {
System.out.println("这里将调用ServiceA的a1方法");
serviceA.a1();
}
}
当你运行这个SpringBoot应用的时候,会遇到一个错误,错误信息如下:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?
还记得Bean的创建过程吗?Spring 在启动时会根据配置文件或注解来创建和初始化所有的bean。这个过程可以分为几个阶段:
@PostConstruct
注解的方法或 InitializingBean
接口的 afterPropertiesSet
方法)。现在我们有两个bean:ServiceA
和 ServiceB
,它们相互依赖对方。具体来说:
ServiceA
依赖 ServiceB
ServiceB
依赖 ServiceA
当Spring尝试创建 ServiceA
时,它会发现 ServiceA
需要 ServiceB
。于是Spring开始创建 ServiceB
。然而,在创建 ServiceB
的过程中,Spring 又发现 ServiceB
需要 ServiceA
。这时,Spring 发现自己已经在一个创建 ServiceA
的过程中,从而导致了一个循环依赖。
好比这张图一样,把箭头的方向可以理解成前提条件,是不是就一目了然了。彼此成为对方的前提条件。就好比,不考虑进化论,究竟是先鸡还是先蛋?
在开发中,一般遇到这个问题,通常会使用@Lazy来解决。
@Service
public class ServiceB implements Service{
@Autowired
@Lazy
private ServiceA serviceA;
@Override
public void b1() {
System.out.println("当前是ServiceB的b1方法");
}
@Override
public void b2() {
System.out.println("这里将调用ServiceA的a1方法");
serviceA.a1();
}
}
它一方面可以减少Spring的IOC容器在启动时的加载时间,一方面也可以解决Bean的循环依赖问题。
但是这是在日常开发使用的时候的处理方法,面试的时候肯定不会就这么放过你。
所以我们在面试的时候遇到这个问题,通常还会再多回答两个方式。
这是一种依赖Spring提前暴露对象的方式来实现的。这种也叫半成品对象,通过对上面的学习,我们知道了循环依赖的原因是因为在创建的时候需要引用到另一个正在创建的对象,通过暴露这种半成品对象,让初始化的时候能够解决循环依赖的问题。
但是这种方式不能使用在原型对象的创建和初始化!背过面试题的都知道:
Spring无法解决构造函数导致的循环依赖,是因为在对象实例化的过程中,构造函数都是最早被调用的,那个时候对象还没完成实例化,所以没办法注入一个尚未完成创建的对象。
因此,解决循环依赖的一种方式,就是避开构造函数注入。
上面的知识只是给你科普用的,不是让你用来回答的。如果你实在不理解,那就背下面的吧!