https://blog.csdn.net/woailaopoqq/article/details/113846328
上面文章介绍了Spring IOC的过程,但是文章中提到的代码未解决循环依赖,本文重点介绍Spring如何解决循环依赖。
上代码:https://download.csdn.net/download/woailaopoqq/15380786
1、模型:存在类关系 A->B->C->A
public class A {
private String id;
private B b;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private String id;
private C c;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
}
public class C {
private String id;
private A a;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
2、XML 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- <!DOCTYPE web-app-config SYSTEM 'file:///c:/rj/controller/WEB-INF/web-app-config-1-0.dtd'> -->
<beans>
<!-- =========================================================== -->
<!-- Global Message source. For all servlets. -->
<!-- =========================================================== -->
<bean name="messageSource"
class="com.interface21.context.support.ResourceBundleMessageSource">
<property name="basename">messages</property>
</bean>
<bean name="a" class="com.travelsky.ebuild.spring.A"
sigleton="true">
<property name="id">A</property>
<property name="b" beanRef="true" >b</property>
</bean>
<bean name="b" class="com.travelsky.ebuild.spring.B"
sigleton="true">
<property name="id">B</property>
<property name="c" beanRef="true" >c</property>
</bean>
<bean name="c" class="com.travelsky.ebuild.spring.C"
sigleton="true">
<property name="id">C</property>
<property name="a" beanRef="true" >a</property>
</bean>
</beans>
3、解决循环依赖
Spring IOC 过程:
step1:读取xml配置文件,将xml中bean信息解析到BeanDefinition,供后续使用;
step2:通过clazz.newInstance()实例化bean;
step3:利用step1中读取的配置,找到bean-name,bean-setproperty(最终是setProperty) 等信息,通过反射技术对step2生成的bean进行set property;
step4:通过增加两个Map,来解决循环依赖,具体参考代码中说明,最好的方法是将代码运行起来,Debug
/** 缓存创建完毕的Spring Bean */
private HashMap sharedInstanceCache = new HashMap();
// 缓存clazz.newInstance()实例化的bean
private Map<String, Object> cacheMap = new ConcurrentHashMap<>(16);
1、测试入口类
@Test
public void testABCA()
{
BeanFactory factory = new XmlBeanFactory("applicationContext-ABCA.xml");
A a = (A)factory.getBean("a");
B b = (B)factory.getBean("b");
C c = (C)factory.getBean("c");
}
2、实例化XmlBeanFactory
public XmlBeanFactory(String filename) throws BeansException {
try {
logger.info("Loading XmlBeanFactory from file '" + filename + "'");
loadBeanDefinitions(new FileInputStream(filename));
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Can't open file [" + filename + "]", ex);
}
}
3、解析xml中的Bean信息,bean名称,class名称以及property信息,并存储在BeanDefinition中
private void loadBeanDefinitions(Document doc) throws BeansException {
Element root = doc.getDocumentElement();
logger.fine("Loading bean definitions");
NodeList nl = root.getElementsByTagName(BEAN_ELEMENT);
logger.fine("Found " + nl.getLength() + " <" + BEAN_ELEMENT + "> elements defining beans");
for (int i = 0; i < nl.getLength(); i++) {
Node n = nl.item(i);
loadBeanDefinition((Element) n);
}
// Ask superclass to eagerly instantiate singletons
preInstantiateSingletons();
} // loadBeanDefinitions (Document)
4、解决循环依赖
private final synchronized Object getSharedInstance(String name) throws BeansException {
BeanDefinition bd = getBeanDefinition(name);
Class beanClass = bd.getBeanClass();
// Spring Bean是否存在于sharedInstanceCache中
Object single = this.sharedInstanceCache.get(name);
if (single != null) {
return single;
}
// Spring Bean 是否已经执行过clazz.newInstance(),但是未设置property
single = cacheMap.get(name);
if (single != null) {
return single;
}
//如两个缓存中未查到,则执行过clazz.newInstance(),创建对象,未设置property
Object beanInstance = BeanUtils.instantiateClass(beanClass);
// 放入缓存,当设置property时,如碰到循环依赖需要set该property,则使用此时申城的property
cacheMap.put(name, beanInstance);
// IOC,执行Spring Bean的set方法设置属性
Object o = createBean(name, beanInstance);
//将IOC完毕的Spring Bean放入缓存
sharedInstanceCache.put(name, beanInstance);
//移除中间态
cacheMap.remove(name);
return o;
}