一、什么是循环依赖?
从字面理解上,就是对象A依赖对象B的同时,对象B也依赖上了对象A。
具体来讲,指的是Bean与Bean之间的依赖关系,循环依赖指的是两个或者多个Bean相互依赖。
简单来讲,如下图关系所示:
代码显示:(注解形式)
一、创建dao(数据访问层)
1.1 接口代码
public interface IAccountDao {
public void addAcc(Account account);
}
1.2 接口实现类代码
@Repository
public class AccountDaoImp implements IAccountDao {
// DBUtil提供核心类QueryRunner
// QueryRunner提供增删改 update();查询query();
// 设置成员变量
@Autowired
private QueryRunner queryRunner;
// 构造QueryRunner set方法
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
// 新增方法
public void addAcc(Account account) {
try {
queryRunner.update("insert into account(aname,amoney) value (?,?)", account.getAname(), account.getAmoney());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
============================ 分割线 ===============================
二、创建service(业务层)
2.1 接口代码
public interface IAccountService {
// 增删改查方法
public void addAcc(Account accoount);
}
2.2 接口实现类代码
@Service
public class AccountServiceImp implements IAccountService {
@Autowired//注入bean
private IAccountDao dao;
// set方法
public void setDao(IAccountDao dao) {
this.dao = dao;
}
}
============================ 分割线 ===============================
三、创建controller(控制器)
3.1 接口代码
public interface IAccountController {
public void addAcc(Account account);
}
3.2 接口实现类代码
@Controller
public class AccountControllerImp implements IAccountController {
//设置属性
@Autowired
private IAccountService service;
// set方法
public void setService(IAccountService service) {
this.service = service;
}
}
当然,上述三层结构是最常见的一种循环依赖,比较特殊的还有:“自己依赖自己”
// 自己依赖自己
@Component
public class A {
// A中注入了A
@Autowired
private A a;
}
配置文件(格式)
<bean>
<!--======================bean的自动装配==========================-->
<!--注入dao-->
<bean id="b" class="com.apesource.dao.AccountDaoImp">
</bean>
<!--注入service-->
<bean id="a" class="com.apesource.service.AccountServiceImp">
</bean>
<!--注入controller-->
<bean id="controllerImp" class="com.apesource.controller.AccountControllerImp">
</bean>
</beans>
二、什么情况下循环依赖可以被处理?
使用前提:
循环依赖包括: 1、构造器注入循环依赖 set , 注入循环依赖 和 prototype模式Bean的循环依赖。
2、出现循环依赖的Bean必须要是单例
三、Spring是如何解决的循环依赖?
嵌入代码:
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
Spring在创建Bean的过程中分为三步
-
实例化,对应方法:
AbstractAutowireCapableBeanFactory
中的createBeanInstance
方法 -
属性注入,对应方法:
AbstractAutowireCapableBeanFactory
的populateBean
方法 -
初始化,对应方法:
AbstractAutowireCapableBeanFactory
的initializeBean
Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建实体类A, 具体是:
-
实例化,简单理解就是new了一个对象
-
属性注入,为实例化中new出来的对象填充属性
-
初始化,执行aware接口中的方法,初始化方法,完成
AOP
代理
创建实体类A的过程实际上就是调用getBean()
方法,这个方法有两层含义
-
创建一个新的Bean
-
从缓存中获取到已经被创建的对象
调用getSingleton(beanName)
首先调用getSingleton(a)
方法,这个方法又会调用getSingleton(beanName, true)
getSingleton(beanName, true)
这个方法实际上就是到缓存中尝试去获取Bean,整个缓存分为三级
-
singletonObjects
,一级缓存,存储的是所有创建好了的单例Bean -
earlySingletonObjects
,完成实例化,但是还未进行属性注入及初始化的对象 -
singletonFactories
,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
因为A是第一次被创建,所以不管哪个缓存中必然都是没有的,注入就发生在第二步,属性赋值;对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,结合这个过程,Spring为了解决单例的循环依赖问题,使用了三级缓存。
Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
这三级缓存分别指:
1、一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
2、二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例
3、三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
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;
}
...
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
...
}
拿bean的时候先从一级缓存singletonObjects中获取;
1、 如果获取不到或者对象正在创建中,就从二级缓存earlySingletonObjects中获取;
2、 如果还是获取不到就从三级缓存singletonFactories中获取,然后将获取到的对象放到二级缓存earlySingletonObjects中,并且将bean对应的三级缓存singletonFactories清除;
3、 bean初始化完毕,放到一级缓存singletonObjects中,将bean对应的二级缓存earlySingletonObjects清除
总结:
Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects
),二级缓存为早期曝光对象earlySingletonObjects
,三级缓存为早期曝光对象工厂(singletonFactories
)。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
===========注释=============
因本人能力有限,本文章部分内容参考于【程序员DMZ 】作者的《讲一讲Spring中的循环依赖》一文。