Spring IoC 和 DI 介绍

1. 概述

在本文中,我们将介绍IoCInvention of Control - 控制反转)和DIDependency Injection - 依赖注入)的概念,然后我们将看看这些如何在Spring框架中实现

2. 什么是控制反转?

控制反转是软件工程中的一个原则,该原则就是将对象或程序的某些部分的控制权转移到容器或框架。它最常用于面向对象编程的环境中。

与我们自定义代码调用库的传统编程相比,IoC能使框架控制程序流程并调用我们的自定义代码。To enable this, frameworks use abstractions with additional behavior built in。如果我们想添加自己的行为,我们需要扩展框架的类或者插入我们自己的类。

这种架构的优点是:

  • 将任务的执行从其实现中解耦出来
  • 使其更容易在不同的实现之间切换
  • 程序的模块化程度更高
  • 通过隔离组件或模拟其依赖性并允许组件通过约定进行通信可更容易地测试程序

控制反转可以通过各种机制来实现,如策略设计模式(Strategy design pattern),服务定位器模式(Service Locator Pattern),工厂模式(Factory pattern)和依赖注入(DI)。

3.什么是依赖注入?

依赖注入是实现IoC的一种模式,其中被颠倒的控制是对象依赖关系的设置。

连接对象与其他对象或将对象“注入”其他对象的操作由an assembler完成,而不是由对象本身完成。

以下是您如何在传统编程中创建对象依赖关系的方法:

public class Store {
    private Item item;

    public Store() {
        item = new ItemImpl1();    
    }
}

在上面的例子中,我们需要在Store类本身内实例化Item接口的实现。

通过使用DI,我们可以重写示例,而不必指定我们想要的Item的实现:

public class Store {
    private Item item;
    public Store(Item item) {
        this.item = item;
    }
}

在接下来的部分中,我们将看到我们如何通过元数据提供Item的实现。

IoCDI都是简单的概念,但对于我们构建系统的方式有着深刻的影响,所以它们非常值得理解。

4. Spring IoC容器

IoC容器是实现IoC的框架的共同特征。
Spring框架中,IoC容器由接口ApplicationContext表示。Spring容器负责实例化,配置和组装对象,这些对象被称为beans,并管理它们的生命周期。

Spring框架提供了几个ApplicationContext接口的实现 — 用于独立应用程序的ClassPathXmlApplicationContextFileSystemXmlApplicationContext,以及用于Web应用程序的WebApplicationContext
为了组装beans,容器使用配置元数据,可以采用XML配置或注解的形式。
以下是手动实例化容器的一种方法:

ApplicationContext context
  = new ClassPathXmlApplicationContext("applicationContext.xml");

5. Spring中的依赖注入

要在上面的示例中设置item属性,我们可以使用元数据。然后,容器将读取这个元数据并在运行时使用它来组装beansSpring中的依赖注入可以通过constructorssettersfields来完成。

5.1 基于Constructor的依赖注入

在基于Constructor的依赖注入的情况下,容器将调用一个构造函数,该构造函数的参数代表我们想要设置的依赖项

Spring解析每个参数主要通过类型,接着是属性名称和索引来消除歧义。让我们看看使用注解的bean及其依赖关系的配置:

@Configuration
public class AppConfig {

    @Bean
    public Item item1() {
        return new ItemImpl1();
    }

    @Bean
    public Store store() {
        return new Store(item1());
    }
}

@Configuration注解表明该类是bean定义的来源。另外,我们可以将它添加到多个配置类中。
@Bean注释用于定义bean的方法。如果我们不指定自定义名称,那么bean名称将默认为方法名称。
对于具有默认singleton作用域的beanSpring首先检查一个已经存在的bean的缓存实例是否已经存在,并且只有在没有时才创建一个新实例。
如果我们使用prototype作用域,容器为每个方法调用返回一个新的bean实例。

另一种创建bean配置的方法是通过XML配置:

<bean id="item1" class="org.baeldung.store.ItemImpl1" /> 
<bean id="store" class="org.baeldung.store.Store"> 
    <constructor-arg type="ItemImpl1" index="0" name="item" ref="item1" /> 
</bean>

5.2 基于Setter的依赖注入

对于基于Setter的依赖注入,容器将在调用无参数构造函数或无参数静态工厂方法来实例化bean之后,调用我们类的setter方法。

我们使用注解创建此配置:

@Bean
public Store store() {
    Store store = new Store();
    store.setItem(item1());
    return store;
}

我们也可以将XML用于相同的bean配置:
同一个bean可以组合基于constructor和基于setter的注入类型。Spring文档建议使用基于constructor的注入来实现强制的依赖关系,以及基于setter的注入来实现可选的注入。

5.3. 基于Field的依赖注入

在基于Field的依赖注入中,我们可以通过使用@Autowired注解标记它们来注入依赖关系:

public class Store {
    @Autowired
    private Item item; 
}

在构造Store对象时,如果没有构造函数或setter方法来注入Item bean,容器将使用反射将Item注入Store

我们也可以使用XML配置来实现这一点。

这种方法看起来更简单,更清洁,但不推荐使用,因为它有一些缺点,例如:

  • 此方法使用反射来注入依赖关系,这比基于constructor或基于setter的注入成本高
  • 使用这种方法继续添加多个依赖关系非常简单。如果您使用的constructor注入具有多个参数,那么我们会认为该类做了不止一件事情可能会违反单一责任原则。(意思大致是添加多个依赖关系简单是简单,但是这样是不是违反了单一责任原则)

5.4 自动装配依赖关系

[Wiring](http://www.baeldung.com/spring-annotations-resource-inject-autowire)允许Spring容器通过检查已定义的bean来自动解决与将要合作bean之间的依赖关系。
这里有四种使用XML配置自动装配bean的模式:

  • no:默认值 - 这意味着没有自动装配用于bean,我们必须明确命名依赖关系。
  • byNameautowiring是基于属性的名称完成的,因此Spring将查找与需要设置的属性同名的bean
  • byType:与byName自动装配类似,仅基于属性的类型。这意味着Spring将查找与要设置的属性类型相同的bean。如果该类型有多个bean,则该框架会引发异常。
  • constructorautowiring基于constructor参数完成,这意味着Spring将查找与constructor参数具有相同类型的bean。

例如,让我们按照类型自动装入上面定义的item1 beanStorebean中:

@Bean(autowire = Autowire.BY_TYPE)
public class Store {

    private Item item;

    public setItem(Item item){
        this.item = item;    
    }
}

我们也可以使用@Autowired注解来注入bean,以便按照以下类型进行自动装配:

public class Store {

    @Autowired
    private Item item;
}

如果有多个相同类型的bean,我们可以使用@Qualifier注释来按名称引用bean

public class Store {

    @Autowired
    @Qualifier("item1")
    private Item item;
}

现在,让我们通过XML配置按类型自动调用bean

<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>

接下来,让我们通过XML将一个名为itembean注入到store beanitem属性中:

<bean id="item" class="org.baeldung.store.ItemImpl1" />

<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>

我们也可以通过contructor参数或setter明确定义依赖关系来覆盖自动装配。

5.5. Beans的懒加载

默认情况下,容器在初始化期间创建并配置所有的singleton beans。为了避免这种情况,您可以在bean配置中使用值为truelazy-init属性:

<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />

因此,item1 bean只会在第一次请求时初始化,而不是在启动时初始化。这样做的好处是更快的初始化时间,但是要权衡的是只有在请求bean之后才能发现配置错误,这可能在应用程序已经运行几个小时甚至几天之后。

6. 结论

在本文中,我们介绍了控制反转和依赖注入的概念,并在Spring框架中对它们进行了例证。
您可以在Martin Fowler的文章中阅读有关这些概念的更多信息:

【参考资料】

  1. Intro to Inversion of Control and Dependency Injection with Spring

猜你喜欢

转载自blog.csdn.net/qq_35070673/article/details/80284743