【IoC】一文搞定Spring依赖注入日常使用以及底层原理

原文链接

一、本文概览

依赖注入的环节发生在:Spring初始化Bean的时候,对该Bean实例的具体字段通过反射的方式进行赋值的操作

在这里插入图片描述

二、什么是依赖注入(DI)

以下是我对依赖注入的理解。

依赖注入,可以通俗的理解为属性填充。但与简单属性填充有些不同。我们知道Spring实现了IoC,也就是控制反转,即将对象实例的控制权进行了反转,Spring替我们创建对象,而初始化对象的过程就称为依赖注入,这个依赖可以基础类型,也可以是引用类型。

三、依赖注入的方式

Spring是通过setter方法注入、构造器注入、自动绑定注入的形式实现依赖注入的,下面先看下是如何使用的,以及这些方式在底层API是如何被实现的!

1、setter方法注入

xml
  • xml配置
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="user" name="user,user2" class="com.markus.spring.ioc.container.domain.User">
        <property name="name" value="markus"/>
        <property name="age" value="23"/>
        <property name="city" value="BEIJING"/>
        <property name="workCities" value="BEIJING,HEZE"/>
        <property name="lifeCities" value="BEIJING,HEZE"/>
      <!--这个可以先忽略-->
<!--        <property name="resource" value="classpath:/META-INF/user-config.properties"/>-->
    </bean>
    <bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder">
      	<!--xml 实现setter方法依赖注入-->
        <property name="user" ref="user"/>
    </bean>
</beans>
  • java代码

UserHolder类

package com.markus.spring.ioc.container.domain;

import com.markus.spring.ioc.container.eunms.City;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.Resource;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;

public class User implements BeanNameAware {
    private Long id;
    private Integer age;
    private String name;
    private City city;  // 枚举类型 ∈ 基础类型(标量类型)
    private City[] workCities;// 数组类型
    private List<City> lifeCities;// 集合类型
    private Resource resource;// Spring类型 ∈ 基础类型

    private transient String beanName;

    public User(){
//        System.out.println("我被初始化了...");
    }
    public User(int age,String name){
        this.age = age;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
         this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    public City[] getWorkCities() {
        return workCities;
    }

    public void setWorkCities(City[] workCities) {
        this.workCities = workCities;
    }

    public List<City> getLifeCities() {
        return lifeCities;
    }

    public void setLifeCities(List<City> lifeCities) {
        this.lifeCities = lifeCities;
    }

    public Resource getResource() {
        return resource;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                ", city=" + city +
                ", workCities=" + Arrays.toString(workCities) +
                ", lifeCities=" + lifeCities +
                ", resource=" + resource +
                '}';
    }

    public static User createUser(){
        System.out.println("我被创建了...");
        User user = new User();
        user.setAge(23);
        user.setName("Markus");
        return user;
    }

    public static User createUser(int age,String name){
        User user = new User();
        user.setAge(age);
        user.setName(name);
        return user;
    }

    @PostConstruct
    public void init(){
        System.out.println("["+beanName+"] Bean 正在初始化...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("["+beanName+"] Bean 正在销毁...");
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

package com.markus.spring.spring.dependency.injection;
import com.markus.spring.ioc.container.domain.User;
import java.util.Collection;
/**
 * @author: markus
 * @date: 2022/3/19 11:27 下午
 * @Description: 管理User对象的Bean
 * @Blog: http://markuszhang.com/
 */
public class UserHolder {
    private User user;

    private Collection<User> users;

    public UserHolder(){

    }

    public UserHolder(User user){
        this.user = user;
    }

    public UserHolder(User user, Collection<User> users) {
        this.user = user;
        this.users = users;
    }

    public Collection<User> getUsers() {
        return users;
    }

    public void setUsers(Collection<User> users) {
        this.users = users;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "UserHolder{" +
                "user=" + user +
                ", users=" + users +
                '}';
    }
}

接下来看下主程序

package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: markus
 * @date: 2022/8/29 10:38 PM
 * @Description: xml方式 setter方法注入 示例
 * @Blog: http://markuszhang.com/doc-blog/
 * It's my honor to share what I've learned with you!
 */
public class XmlDependencySetterInjectionDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-setter-injection.xml");

        UserHolder userHolder = context.getBean(UserHolder.class);
        System.out.println(userHolder);
    }
}
注解
package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.ioc.container.domain.User;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

/**
 * @author: markus
 * @date: 2022/9/2 10:13 PM
 * @Description: 注解驱动 setter依赖注入
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class AnnotationDependencySetterInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencySetterInjectionDemo.class);

      	// 加载BeanDefinition
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(context);
        beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");

      	// 启动容器
        context.refresh();

      	// @Bean已经将UserHolder注册到Spring中,我们只需要获取它,Spring内部去创建
        UserHolder userHolder = context.getBean(UserHolder.class);
        System.out.println(userHolder);
      
				// 关闭容器
        context.close();
    }

    @Bean
    public UserHolder userHolder(User user) {
        UserHolder userHolder = new UserHolder();
  	   	// setter注入
        userHolder.setUser(user);
        return userHolder;
    }
}

依赖的User Bean配置

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="user" name="user,user2" class="com.markus.spring.ioc.container.domain.User">
       <property name="name" value="markus"/>
       <property name="age" value="23"/>
       <property name="city" value="BEIJING"/>
       <property name="workCities" value="BEIJING,HEZE"/>
       <property name="lifeCities" value="BEIJING,HEZE"/>
       <property name="resource" value="classpath:/META-INF/user-config.properties"/>
   </bean>

</beans>
接口回调

接口回调这里,就是Spring内嵌接口,我们可以通过回调的方式将这些接口实例注入到我们自己的实例中;

其中能实现接口回调注入的Bean依次为:

  • BeanFactory
  • ApplicationContext
  • MessageResource
  • Environment
  • EmbeddedValueResolver
  • ApplicationEventPublisher

下面用BeanFactory举下例子:

先实现BeanFactoryAware接口

package com.markus.spring.spring.dependency.injection;

import com.markus.spring.ioc.container.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import java.util.Collection;

/**
 * @author: markus
 * @date: 2022/3/19 11:27 下午
 * @Description: 管理User对象的Bean
 * @Blog: http://markuszhang.com/
 */
public class UserHolder implements BeanFactoryAware {

    private BeanFactory beanFactory;
  	// other filed

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
  	// other field getter setter...
}

package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: markus
 * @date: 2022/9/2 10:28 PM
 * @Description: 接口回调接口 setter方法注入 示例
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class AwareCallbackDependencySetterInjectionDemo {
    public static void main(String[] args) {
        // 我们借用ClassPathXmlApplicationContext 将UserHolder Bean定义到xml中
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-setter-injection.xml");

        UserHolder userHolder = context.getBean(UserHolder.class);
        // 这里我们看下UserHolder中注入的beanFactory是否和Spring内部BeanFactory一致
        System.out.println(userHolder.getBeanFactory() == context.getBeanFactory());
//        主程序输出
//        true
//        Process finished with exit code 0
    }
}
底层API

我们看到上面又是通过xml实现setter方法注入,又是通过注解的方式实现setter方法注入,那他们最终转到底层都是通过BeanDefinitionBuilder.addPropertyReference(String,String)实现的,我们来看下代码实现:

package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: markus
 * @date: 2022/9/2 10:39 PM
 * @Description: 底层Api实现 setter方法依赖注入
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class ApiDependencySetterInjectionDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");

        // 创建BeanDefinition
        BeanDefinition beanDefinition = createBeanDefinition();
        // 注册BeanDefinition
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
        beanFactory.registerBeanDefinition("userHolder", beanDefinition);

        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);

        applicationContext.close();
        // 控制台输出
//      UserHolder{user=User{id=null, age=23, name='markus', city=BEIJING, workCities=[BEIJING, HEZE], lifeCities=[BEIJING, HEZE], resource=class path resource [META-INF/user-config.properties]}, users=null}
//      Process finished with exit code 0
    }

    private static BeanDefinition createBeanDefinition() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        builder.addPropertyReference("user", "user");
        return builder.getBeanDefinition();
    }
}

2、构造器注入(官方推荐使用)

xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <import resource="dependency-lookup.xml"/>
    <bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder">
        <constructor-arg name="user" ref="user"/>
    </bean>
</beans>
package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: markus
 * @date: 2022/9/2 10:07 PM
 * @Description: 构造器 依赖注入
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class XmlDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-constructor-injection.xml");

        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);
        applicationContext.close();
    }
}
注解
package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.ioc.container.domain.User;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

/**
 * @author: markus
 * @date: 2022/9/4 11:28 AM
 * @Description: 注解驱动 构造器注入 示例
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class AnnotationDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencySetterInjectionDemo.class);

        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(context);
        beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");

        context.refresh();

        UserHolder userHolder = context.getBean(UserHolder.class);
        System.out.println(userHolder);

        context.close();
    }

    @Bean
    public UserHolder userHolder(User user) {
        // 构造器注入
        UserHolder userHolder = new UserHolder(user);
        return userHolder;
    }
}
底层API
package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: markus
 * @date: 2022/9/4 12:02 PM
 * @Description: 底层api 实现依赖构造器注入 示例
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class ApiDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");

        // 构建BeanDefinition
        BeanDefinition userHolderBeanDefinition = createBeanDefinition();
        // 注册BeanDefinition
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
        registry.registerBeanDefinition("userHolder", userHolderBeanDefinition);

        // 依赖查找 UserHolder
        UserHolder userHolder = context.getBean("userHolder", UserHolder.class);
        System.out.println(userHolder);

        // 关闭应用上下文
        context.close();

    }

    private static BeanDefinition createBeanDefinition() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        // 底层API实现构造器注入
        builder.addConstructorArgReference("user");
        return builder.getBeanDefinition();
    }
}

3、自动绑定注入

自动绑定注入有以下三种:

  • byType: 通过类型注入
  • byName: 通过名称注入
  • constructor: 构造器注入

下面我们看下例子

ps: 自动注入可以通过xml、注解形式实现。xml通过bean标签的autowire指定实现,注解则是通过@Autowired、@Resource等注解方式实现。

xml

<!--通过类型注入-->
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="byType"/>
<!--通过名称注入-->
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="byName"/>
<!--通过构造器注入-->
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="constructor"/>

注解

package com.markus.spring.spring.dependency.injectionv2;

import com.markus.spring.ioc.container.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

/**
 * @author: markus
 * @date: 2022/9/4 2:18 PM
 * @Description: 自动绑定注入 字段、方法注入
 * @Blog: http://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class AutowiringDependencyInjectionDemo {

    @Autowired
    private User user; // 注入的是 superUser (@Autowired 首先按照类型注入,如果找到多个就选被primary标注的bean注入,如果没标注,就按照名称选择)

    @Resource
    private User user1; // 注入的是 superUser

    @Autowired
    private Collection<User> users; // 注入的是 user superUser

    @Autowired
    private Map<String,User> userMap; // 注入的是 user superUser

    @Autowired
    private Optional<User> userOptional; // 注入的是 superUser

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AutowiringDependencyInjectionDemo.class);

        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) context.getBeanFactory());
        reader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");

        context.refresh();

        AutowiringDependencyInjectionDemo demo = context.getBean(AutowiringDependencyInjectionDemo.class);
        System.out.println(demo.user);
        System.out.println(demo.user1);
        System.out.println(demo.users);
        System.out.println(demo.userMap);
        System.out.println(demo.userOptional);
        
        context.close();
    }
}

其实自动绑定底层最终也是通过setter方法或者构造器实现依赖注入的。

四、依赖注入的原理

前面说了什么是依赖注入以及怎么使用这一特性,接下来这一章节就来说说它的内部原理是怎样的。

1、原理流程概览

先来看下整体关于Bean初始化的概览图
在这里插入图片描述

通过上图来看,依赖注入的功能点表现在populateBean()方法内,它表现在拿到依赖对象并赋值到当前实例的字段中。

对于依赖注入的原理,有几个比较重要的类以及方法罗列如下:

  • DefaultListableBeanFactory
    • 依赖解析的入口:resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)
    • 依赖解析的具体动作:doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)
    • 解析依赖字段为多个Bean的情况:resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)
    • 查找符合注入类型Bean对象:Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor)
    • 决定最终需要注入的对象:String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor)
  • AutowiredAnnotationBeanPostProcessor
    • 实现依赖注入的地方:PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    • 构建注入数据源缓存:postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
      • InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)
  • CommonAnnotationBeanPostProcessor
    • 实现依赖注入的地方:PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    • 构建注入数据源缓存:postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
      • InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)

2、DefaultListableBeanFactory#resolveDependency()

在这里插入图片描述

3、@Autowired & @Resource实现原理

根据上面的描述,大家应该对这两个底层原理有了大致的了解,只不过在细节上会有一些不同:

  • @Autowired依赖AutowiredAnnotationBeanPostProcessor实现
  • @Resource依赖CommonAnnotationBeanPostProcessor实现

这两个BenaPostProcessor后置处理器有两个生命周期需要提及一下:

  • postProcessMergedBeanDefinition
    • 在合并BeanDefinition后的调用,合并BeanDefinition是指将多个GenericBeanDefinition(例如在xml配置中的有继承关系的Bean)合并生成一个RootBeanDefinition
    • 调用的目的主要是生成依赖注入元数据InjectionMetadata缓存用于依赖注入
  • postProcessProperties
    • 在属性填充时调用,用于解析相应的依赖对象并通过反射进行属性填充

五、总结

以上就是我对依赖注入的学习记录,其实流程很简单,多读几遍源码就能理顺清楚。主要记住几个关键类以及他们之间的调用关系即可

  • DefaultListableBeanFactory
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • InjectionMetadata
  • InjectedElement
    • AutowiredFieldElement
    • AutowiredMethodElement
    • ResourceElement
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值