问题呈现
一个容器可以存在父容器,比如定义一个 Book 类
java
public class Book {
private String name;
private Double price;
private Integer id;
// set get toString...
}
定义两个 XML 文件,一个是父容器的,一个是子容器的:
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.example.demo.Book" id="book" >
<property name="id" value="111" />
<property name="name" value="刘和广" />
<property name="price" value="77d" />
</bean>
</beans>
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.example.demo.Book" id="book" >
<property name="id" value="222" />
<property name="name" value="刘和广之子" />
<property name="price" value="77d" />
</bean>
</beans>
现在去加载两个容器,注意必须手动 setConfigLocations
,配置文件不能放在 ClassPathXmlApplicationContext
构造方法
当从 child 中查找 bean 的时候,首先去 child 容器中查找,这个容器中如果不存在该 bean,那么就去父容器中查找,父容器中不存在,就去爷爷容器中查找。。。
java
public class App {
public static void main(String[] args) {
// 父子容器
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext();
parent.setConfigLocations("parent.xml");
parent.refresh();
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext();
child.setConfigLocations("child.xml");
child.setParent(parent);
child.refresh();
Book book = child.getBean("book", Book.class);
System.out.println("book = " + book);
}
}
原理分析
要实现父子容器的效果,有一个前提,就是我们的容器本身要支持父子容器,而我们日常开发中最常用的 DefaultListableBeanFactory
这个容器,就是支持父子容器的:
HierarchicalBeanFactory 接口就表示带有层级关系的父子容器了,实现了该接口,就表示这个容器带有层级关系,即具备父子容器的功能。那么接下来,在 Bean 查找的过程中,首先就在当前容器中查找,如果找不到,就去父容器中查找。
根据类型查找,下面这个 getBean 方法最终会调用到 org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveBean
方法:
text
Book book = child.getBean( Book.class);
java
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
//这里就是尝试在当前容器中去查找这个目标 bean
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
//如果当前容器中没有找到 Bean,则去查找到父容器,然后去父容器中查找 bean
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory dlfb) {
//父容器存在,并且是 DefaultListableBeanFactory 类型的,那么则调用 resolveBean 方法去处理 bean,这其实相当于递归
return dlfb.resolveBean(requiredType, args, nonUniqueAsNull);
} else if (parent != null) {
//父容器不是 DefaultListableBeanFactory 类型,但是父容器又存在,那么尝试调用父容器中的 getBeanProvider 方法去获取 bean
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
} else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}
根据名字去查找 bean,最终会来到 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法中:
text
Book book = child.getBean("book", Book.class);
java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
...
else{
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 子容器不存在,去父容器查找
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory abf) {
return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
} else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
} else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}