Spring 依赖注入的方式,你了解哪些?

简介: 前言依赖查找和依赖注入是 Spring 实现 IoC 容器提供的两大特性,相对于依赖查找,Spring 更推崇的是使用依赖注入,本篇先对 Spring 中依赖注入的几种方式进行介绍,后续再分享其实现。

前言


依赖查找和依赖注入是 Spring 实现 IoC 容器提供的两大特性,相对于依赖查找,Spring 更推崇的是使用依赖注入,本篇先对 Spring 中依赖注入的几种方式进行介绍,后续再分享其实现。


依赖注入方式


如果在 A 类中使用到了 B 类的实例,那么我们就说 A 依赖 B。依赖的位置包括字段、构造器方法参数、普通方法参数等,Spring 都对其进行了支持。Spring 针对依赖提供了自动依赖注入和手动依赖注入两种方式,下面对其进行介绍。


自动依赖注入


自动依赖注入是 Spring 早期提供的能力,使用自动依赖注入可以避免显式的配置属性和构造函数参数的依赖,Spring 中提供了四种自动依赖注入的模式。具体如下。


image.png


Spring 早期曾经有一个 autodetect 的自动依赖注入方式,用于自动探测依赖注入方式,如果存在带参数的构造方法则使用 constructor 自动依赖注入,否则使用 byType 自动依赖注入,在 Spring 3.0 已废弃。


自动依赖注入可以通过 XML 配置文件或 @Bean 注解中进行指定。

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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--autowire 可指定 no、default、byName、byType、constructor-->
    <bean class="com.zzuhkp.Bean" autowire="no"/>
</beans>


@Bean 指定自动依赖注入的示例如下。


@Configuration
public class Config {
  // 通过 @Bean 的 autowire 属性指定自动依赖注入,可以指定 Autowire.NO、Autowire.BY_NAME、Autowire.BY_TYPE
    @Bean(autowire = Autowire.NO)
    String bean() {
        return "zzuhkp";
    }
}


自动依赖注入的优势


任何事物都有两面性,那么自动依赖注入自然也有其优势和限制。自动依赖注入优点如下。


自动依赖注入避免了指定属性和构造器参数。

在类中添加新的依赖项时,不必修改依赖,在开发中比较有用。

自动依赖注入的限制

虽然自动依赖注入避免了显式指定依赖项,但是其仍有一些不足,因此 Spring 默认不开启自动依赖注入。其具备的一些限制如下。


显式设置的属性和构造方法参数依赖将会覆盖自动依赖注入,无法装配简单的属性,如基本类型、String、Class 等。

自动依赖注入将导致对象之间的关系不如显式依赖注入清晰。

自动依赖注入将影响从 Spring 容器中生成文档的工具。

byType 类型的自动依赖注入,如果存在多个类型相同的 bean 不会影响注入数组、集合、Map 等,而注入单个 bean 对象由于歧义 Spring 则会抛出异常。

手动依赖注入

手动依赖注入相对自动依赖注入来说比较灵活,手动依赖注入可以通过 XML 配置文件、Java 注解或 Spring 提供的 API 来完成,而从具体依赖注入的方法来说又包括 setter、字段、构造器、方法、接口回调,下面分别予以说明。


setter 依赖注入


setter 依赖注入是通过调用 bean 的 setXXX 方法设置 bean 的依赖。自动依赖注入时设置注入类型为 byName 或 byType 可以达到 setter 注入的目的,而手动依赖注入则可以通过 XML 手动配置配置依赖或通过 Spring API 编程设置。


XML 手动 setter 依赖注入示例如下。


<?xml version="1.0" encoding="UTF-8"?>
<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 class="com.zzuhkp.Bean">
        <!--设置 setter 依赖注入-->
        <property name="prop" ref="prop2"/>
    </bean>
    <bean id="prop" class="com.zzuhkp.Prop">
        <property name="name" value="this is name"/>
        <property name="value" value="this is value"/>
    </bean>
</beans>


Spring API setter 依赖注入示例如下,和上述的 XML 配置向对应。


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 手动创建 BeanDefinition
        AbstractBeanDefinition prop = BeanDefinitionBuilder.genericBeanDefinition(Prop.class)
            // 添加 setter 注入的属性值
            .addPropertyValue("name", "this is name")
            .addPropertyValue("value", "this is value")
            // 获取 BeanDefinition
            .getBeanDefinition();
        // 向 Spring 中注入
        context.registerBeanDefinition("propBeanName", prop);
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Bean.class)
            .addPropertyReference("prop", "propBeanName")
            .getBeanDefinition();
        context.registerBeanDefinition("beanName", bean);
        context.refresh();
        System.out.println(context.getBean("beanName"));
        context.close();
    }
}


Spring API setter 依赖注入示例中通过创建 BeanDefinition,并设置 BeanDefinition 中的属性来达到 setter 依赖注入的目的。关于 BeanDefinition 的知识参见 《掌握 Spring 必须知道的 BeanDefinition》 。


字段依赖注入


字段依赖注入是通过反射设置对象的字段值来实现。字段依赖注入通过在 bean 的字段上添加 @Autowired 、@Resource 或者 @Inject 来完成。关于 @Autowired 和 @Resource 的区别,可参见前面的文章 《Spring 中 @Autowired 和 @Resource 有什么区别?》。


字段依赖注入的示例如下。


@Component
public class Bean {
  // @Resource
    @Autowired
    private Prop prop;
    public Prop getProp() {
        return prop;
    }
    public Bean setProp(Prop prop) {
        this.prop = prop;
        return this;
    }
    @Override
    public String toString() {
        return "Bean{" +
            "prop=" + prop +
            '}';
    }
}


构造器依赖注入


构造器依赖注入是注入 Spring Bean 实例化时需要的参数。可以指定自动依赖注入的方式为 constructor 实现,也可以指定构造器参数、通过 @Autowired 注解或者 Spring API 手动依赖注入。


通过 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="com.zzuhkp.Bean">
        <!--构造器依赖注入-->
        <constructor-arg ref="prop"/>
    </bean>
    <bean id="prop" class="com.zzuhkp.Prop">
        <property name="name" value="this is name"/>
        <property name="value" value="this is value"/>
    </bean>
</beans>


通过 @Autowired 进行构造器依赖注入示例如下。


public class Bean {
    private Prop prop;
    public Bean(@Autowired Prop prop) {
        this.prop = prop;
    }
}

通过 Spring API 进行手动构造器依赖注入的示例如下。


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 手动创建 BeanDefinition
        AbstractBeanDefinition prop = BeanDefinitionBuilder.genericBeanDefinition(Prop.class)
            // 添加 setter 注入的属性值
            .addPropertyValue("name", "this is name")
            .addPropertyValue("value", "this is value")
            // 获取 BeanDefinition
            .getBeanDefinition();
        // 向 Spring 中注入
        context.registerBeanDefinition("prop", prop);
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Bean.class)
            // 添加构造器所需的依赖
            .addConstructorArgReference("prop")
            .getBeanDefinition();
        context.registerBeanDefinition("bean",bean);
        context.refresh();
        System.out.println(context.getBean("bean"));
        context.close();
    }
}


方法依赖注入


方法依赖注入和 setter 依赖注入类似,但是不限制方法必须为 setter 方法。方法依赖注入只能通过注解完成,在方法上添加 @Autowired、@Resource 或者 @Inject 可以为只有一个参数的方法进行依赖注入。配置类中 @Bean 注解定义的 bean 则会按照 constructor 自动依赖注入的方式进行处理。


方法依赖注入的示例如下。


@Component
public class Bean {
    private Prop prop;
    public Bean(@Autowired Prop prop) {
        this.prop = prop;
    }
    // @Resource
    // @Inject
    @Autowired
    public void prop(Prop prop) {
        this.prop = prop;
    }
}


回调依赖注入


回调依赖注入依托于 Spring Bean 的生命周期,需要实现 Spring 提供的 XxxAware 接口,在 Spring 生命周期的某一阶段会调用接口中定义的方法,从而拿到方法中的参数。以 ApplicationContextAware 为例,示例如下。


@Component
public class Bean implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}


依赖注入的来源


和依赖查找一样,依赖注入也具有自己的来源。与依赖查找不同的是,依赖注入的来源除了 BeanDefinition、单例 bean,还包括 Spring 中一些游离的对象,这些对象非 Spring 管理,Spring 只是简单记录了其引用。此外,通过 @Value 注解,Spring 还能够注入外部化配置。有关外部化配置,可参考文章 《Spring 中的 Environment 、Profile 与 PropertySource》。


游离对象的注册,感兴趣的小伙伴可查阅源码ConfigurableListableBeanFactory#registerResolvableDependency,而依赖注入最终则会委托给AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor,String,Set<String>, TypeConverter) 解析依赖对象,由于其实现相对复杂,本篇不再展开。


总结

依赖注入是我们使用 Spring 最常用的方式,依赖注入包括自动依赖注入和手动依赖注入,具体又分为 setter 依赖注入、字段依赖注入、构造器自动依赖注入、方法依赖注入、接口回调。依赖注入的来源包括 BeanDefinition、单例 bean、游离对象、外部化配置。由于篇幅问题,本篇未对依赖处理过程展开说明,后续再进行分析。


目录
相关文章
|
2月前
|
开发框架 Java Spring
Spring依赖注入以及使用建议
Spring依赖注入以及使用建议
32 0
|
1月前
|
XML Java 数据格式
Spring系列文章2:基于xml方式依赖注入
Spring系列文章2:基于xml方式依赖注入
|
3月前
|
XML Java 程序员
Spring的依赖注入
Spring的依赖注入
|
5月前
|
XML Java 程序员
Spring基础篇:依赖注入
Spring基础篇:依赖注入
|
5月前
|
XML Java 数据格式
深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
在本文中,我们将介绍 IoC(控制反转)和 DI(依赖注入)的概念,以及如何在 Spring 框架中实现它们。
73 0
|
3月前
|
Java Spring
Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入)
Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入)
|
4月前
|
Java API Spring
Spring6-IoC(Inversion of Control)控制反转和DI(Dependency Injection)依赖注入,手动实现IOC
Spring6-IoC(Inversion of Control)控制反转和DI(Dependency Injection)依赖注入,手动实现IOC
|
5月前
|
XML Java 数据格式
Spring注解开发管理第三方bean及依赖注入
Spring注解开发管理第三方bean及依赖注入
59 0
|
13天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
5月前
|
前端开发 Java API
Spring Boot之Spring MVC基于注解的控制器(RequestMapping注解类型 重定向与转发 依赖注入)
Spring Boot之Spring MVC基于注解的控制器(RequestMapping注解类型 重定向与转发 依赖注入)
48 0