Spring BeanFactory 依赖注入

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 就做了三件事,其中属性的查找,尤其是根据类型的查找最为复杂:

  1. 依赖查找。根据 beanName 或 type 查找可注入的依赖值。
  2. 依赖校验。是否所有的字段已经全部匹配上了,根据需要是否要抛出异常
  3. 依赖注入。至些依赖已经全部准备好了,可以进行属性注入。

依赖注入实际上是委托给了 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



每天用心记录一点点。内容也许不重要,但习惯很重要!

原文地址:https://www.cnblogs.com/binarylei/p/10340112.html

时间: 2024-08-14 16:44:32

Spring BeanFactory 依赖注入的相关文章

Spring IoC 依赖注入(二)源码分析

目录 Spring IoC 依赖注入(二)源码分析 1. 依赖注入口 - populateBean 1.1 doCreateBean 1.2 populateBean 2. 手动注入 2.1 相关的类说明 2.2 applyPropertyValues 2.3 BeanDefinitionValueResolver 2.4 依赖检查 2. 自动注入 2.1 那些字段会自动注入 2.2 名称注入 2.3 类型注入 Spring IoC 依赖注入(二)源码分析 本章主要分析 Spring IoC 依

spring中依赖注入方式总结

Spring中依赖注入的四种方式 在Spring容器中为一个bean配置依赖注入有三种方式: · 使用属性的setter方法注入  这是最常用的方式: · 使用构造器注入: · 使用Filed注入(用于注解方式). 使用属性的setter方法注入 首先要配置被注入的bean,在该bean对应的类中,应该有要注入的对象属性或者基本数据类型的属性.例如:为UserBiz类注入UserDAO,同时为UserBiz注入基本数据类型String,那么这时,就要为UserDAO对象和String类型设置se

(spring-第3回)spring的依赖注入-属性、构造函数、工厂方法等的注入

Spring要把xml配置中bean的属性实例化为具体的bean,"依赖注入"是关卡.所谓的"依赖注入",就是把应用程序对bean的属性依赖都注入到spring容器中,由spring容器实例化bean然后交给程序员.spring的依赖注入有属性注入.构造函数注入.工厂方法注入等多种方式,下面用几个简单的栗子来一一道来. 一.首先是属性注入: 代码001 1 <?xml version="1.0" encoding="UTF-8&q

第二十七天 春之细雨润物于无形 —Spring的依赖注入

6月11日,晴."夏条绿已密,朱萼缀明鲜.炎炎日正午,灼灼火俱燃." IT人习惯把具体的事物加工成的形状一致的类,正是这样的一致,加上合适的规范,才能彰显对象筋道的牙感和bean清香的味道.Spring比谁都清楚OO的奥妙,让组件之间的依赖关系由容器在运行时期决定,称作依赖注入(Dependency Injection). 下面用一通俗的例子,一探依赖注入奥妙. 设计模式中的一个原则:针对接口编程,不要针对实现编程. 一.设计两个接口: (1)奶制品接口-MilkProductInte

Spring的依赖注入(DI)三种方式

Spring依赖注入(DI)的三种方式,分别为: 1.  接口注入 2.  Setter方法注入 3.  构造方法注入 下面介绍一下这三种依赖注入在Spring中是怎么样实现的. 首先我们需要以下几个类: 接口 Logic.java 接口实现类 LogicImpl.java 一个处理类 LoginAction.java 还有一个测试类 TestMain.java Logic.java如下: package com.spring.test.di; public interface Logic {

spring的依赖注入的最常见的两种方法

1 package com.lsz.spring.action; 2 3 public class User { 4 /** 5 * set注入 6 */ 7 private String username; 8 public void setUsername(String username) { 9 this.username=username; 10 } 11 public String getUsername() { 12 return username; 13 } 14 /* 15 <b

spring的依赖注入【NC产品中也用到spring的依赖注入】

下面设计到的类有EditPsndocAction跟RefreshPsndocAction这个两个类, 而我想要的最终效果是: 下面解释一下流程“ 如下图所示:因为NC产品中使用了spring的的依赖注入.所以我这里可以这样子调用.直接调用定刷新按钮的bean . 在EditPsndocAction这个类中,用spring的依赖注入的一个方式.给对象赋值 注意一点就是:RefreshPsndocAction 是刷新按钮对应的ben的名称.也就是 最后在EdictPsndocAction这个类的do

二、Spring的依赖注入

Spring的依赖注入 1.理解依赖注入 (1)A对象需要调用B对象的方法,这种情形被称为依赖注入,即A对象依赖B对象:依赖注入(DI)也被成为控制反转(IoC): (2)依赖注入的两种方式: 1)设值注入:IoC容器通过使用成员变量的setter方法来注入被依赖对象: 2)构造注入:IoC容器通过使用构造器来注入被依赖的对象: 2.设置注入 (1)Bean与Bean之间的依赖关系由Spring管理,Spring采用setter方法为目标Bean注入所需要的值,这种注入方式被称为设值注入: (2

关于Spring IOC (依赖注入)你需要知道的一切

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/54561302 出自[zejian的博客] <Spring入门经典>这本书无论对于初学者或者有经验的工程师还是很值一看的,最近花了点时间回顾了Spring的内容,在此顺带记录一下,本篇主要与spring IOC相关 ,这篇博文适合初学者也适合spring有过开发经验的工程师,前者可用于全面了解Spring IOC的知识点,后者且