Spring BeanFactory 依賴註入
阿新 • • 發佈:2019-01-31
auto autowired init mutable populate 構造器 ref prior 屬性註入
Spring BeanFactory 依賴註入
Spring 系列目錄(https://www.cnblogs.com/binarylei/p/10117436.html)
一、autowire 五種註入方式測試
(1) 環境準備
public class Company { private Department department; private List<Employee> employees; public Company() { } public Company(Department department) { this.department = department; } public void setDepartment(Department department) { this.department = department; } public void setEmployees(List<Employee> employees) { this.employees = employees; } } public class Employee { private String name; public void setName(String name) { this.name = name; } } public class Department { private String name; public void setName(String name) { this.name = name; } }
(2) xml 配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="company1" autowire="byName" class="com.github.binarylei.Company"/> <bean id="company2" autowire="byType" class="com.github.binarylei.Company"/> <bean id="company3" autowire="no" class="com.github.binarylei.Company"/> <bean id="company4" autowire="constructor" class="com.github.binarylei.Company"> <constructor-arg index="0" ref="department"/> </bean> <bean id="company5" autowire="default" class="com.github.binarylei.Company"/> <bean id="employee1" class="com.github.binarylei.spring.Employee"> <property name="name" value="employee1"/> </bean> <bean id="employee2" class="com.github.binarylei.spring.Employee"> <property name="name" value="employee2"/> </bean> <bean id="department" class="com.github.binarylei.spring.Department"> <property name="name" value="department"/> </bean> </beans>
(3) 測試一把
@Test public void test() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(lbf); reader.loadBeanDefinitions(new ClassPathResource("spring-context-di.xml", getClass())); // 1. 名稱註入 Company companyByName = (Company) lbf.getBean("company1"); // 2. 類型註入,支持 List 方式註入,如果本地容器找到多個則直接拋出異常 Company companyByType = (Company) lbf.getBean("company2"); // 3. no Company companyByNo = (Company) lbf.getBean("company3"); // 4. 構造器註入 Company companyByConstructor = (Company) lbf.getBean("company4"); // 5. 默認 Company companyDefault = (Company) lbf.getBean("company5"); }
二、Spring 屬性註入源碼分析
2.1 屬性註入 - populateBean
Spring 屬性註入在 populateBean 方法中完成,有兩種註入方式:beanName 或 type 兩種。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException("Cannot apply property values to null instance");
} else {
return;
}
}
// 1. 後置處理器 InstantiationAwareBeanPostProcessor,可以先略過
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
// 2. 依賴查找。根據 beanName 或 type 查找可註入的屬性值。
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
// 3. 後置處理器攔截,對屬性值進行處理 InstantiationAwareBeanPostProcessor
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// 4. 依賴校驗。是否所有的字段已經全部匹配上了,根據需要是否要拋出異常
if (needsDepCheck) {
if (filteredPds == null) {
// 過濾不需要進行屬性註入的字段,如 String、BeanFactory...
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
// 5. 依賴註入。至些屬性已經全部準備好了,可以進行屬性註入。
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
上面的代碼看這很復雜,其實拋開後置處理器 InstantiationAwareBeanPostProcessor 就做了三件事,其中屬性的查找,尤其是根據類型的查找最為復雜:
- 依賴查找。根據 beanName 或 type 查找可註入的依賴值。
- 依賴校驗。是否所有的字段已經全部匹配上了,根據需要是否要拋出異常
- 依賴註入。至些依賴已經全部準備好了,可以進行屬性註入。
依賴註入實際上是委托給了 BeanWrapperImpl 完成,本文的重點的依賴的查找,尤其是根據類型匹配的情況。
2.2 名稱查找 - autowireByName
毫無疑問,直接從 BeanFactory 中取出這個 bean 就可以了
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
}
}
}
2.3 類型查找 - autowireByType
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
if (Object.class != pd.getPropertyType()) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// 類型查找時允許對 FactoryBean 提前實例化對象,大部分情況一都是 true。
// 至於為什麽實現了 PriorityOrdered 接口的 bean 要排除,以後再研究一下。???
boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// 核心代碼就這一句,類型查找委托給了子類的 resolveDependency 完成
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
resolveDependency(desc, beanName, autowiredBeanNames, converter)
可以說是 AbstractAutowireCapableBeanFactory 最重要的模板方法了,子類 DefaultListableBeanFactory 進行了實現,作用就是根據類型查找依賴。
參考:
1 . 《Spring中循環引用的處理》:https://www.iflym.com/index.php/code/201208280001.html
每天用心記錄一點點。內容也許不重要,但習慣很重要!
Spring BeanFactory 依賴註入