Spring 基础知识之IOC介绍

概述

在本文中,咱们将介绍IoC(控制反转)和DI(依赖注入)的概念,而后咱们将看看它们是如何在Spring框架中实现的。编程

什么是控制反转

控制反转是软件工程中的一个原则,经过该原理,对象或程序的一部分的控制被转移到容器或框架。 它最经常使用于面向对象编程的上下文中。设计模式

与咱们的自定义代码调用库的传统编程相比,IoC使框架可以控制程序流并调用咱们的自定义代码。 为了实现这一点,框架使用内置额外行为的抽象。若是咱们想要添加本身的行为,咱们须要扩展框架的类或插入咱们本身的类。缓存

这种架构的优势是:bash

  • 将任务的执行与其实现分离
  • 使得在不一样的实现之间切换更容易
  • 更高程度的模块化
  • 经过隔离组件或模拟其依赖关系并容许组件经过约定进行通讯来更轻松地测试程序

控制反转能够经过各类机制实现,例如:策略设计模式,服务定位模式,工厂模式和依赖注入(DI)。架构

什么是依赖注入

依赖注入是一种实现IoC的模式,其中被反转的控件是对象依赖项的设置。app

将对象与其余对象链接或将对象“注入”其余对象的行为由汇编程序而不是对象自己完成。框架

如下是在传统编程中建立对象依赖项的方法:模块化

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的实现。

Spring IOC 容器

IoC容器是实现IoC的框架的共同特征。

在Spring框架中,IoC容器由接口ApplicationContext表示。 Spring容器负责实例化,配置和组装称为bean的对象,以及管理它们的生命周期。

Spring框架提供了ApplicationContext接口的几个实现 - 用于独立应用程序的ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,以及用于Web应用程序的WebApplicationContext。

为了组装bean,容器使用配置元数据,能够采用XML配置或注释的形式。

ApplicationContext context
  = new ClassPathXmlApplicationContext("applicationContext.xml");
复制代码

要在上面的示例中设置item属性,咱们可使用元数据。 而后,容器将读取此元数据并使用它在运行时组装bean。

Spring中的依赖注入能够经过构造函数,设置器或字段来完成。

基于构造函数的依赖注入

在基于构造函数的依赖项注入的状况下,容器将调用带有参数的构造函数,每一个参数表示咱们要设置的依赖项。

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名称将默认为方法名称。

对于具备默认单例范围的bean,Spring首先检查bean的缓存实例是否已经存在,若是不存在则仅建立新实例。 若是咱们使用原型范围,容器会为每一个方法调用返回一个新的bean实例。

下面是经过xml配置建立bean:

<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>
复制代码

基于Setter的依赖注入

对于基于setter的DI,在调用无参构造函数或无参数静态工厂方法来实例化bean以后,容器将调用咱们类的setter方法。让咱们使用注释建立此配置:

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

相应的xml配置:

<bean id="store" class="org.baeldung.store.Store">
    <property name="item" ref="item1" />
</bean>
复制代码

基于构造函数和基于setter的注入类型能够组合用于同一个bean。Spring文档建议对强制依赖使用基于构造函数的注入,对可选依赖使用基于setter的注入。

基于字段的依赖注入

在基于字段的DI的状况下,咱们能够经过使用@Autowired注释标记它们来注入依赖项:

public class Store {
    @Autowired
    private Item item; 
}
复制代码

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

这种方法可能看起来更简单,更清晰,但不推荐使用,由于它有一些缺点,例如:

  • 此方法使用反射来注入依赖项,这比基于构造函数或基于setter的注入代价要高
  • 使用这种方法继续添加多个依赖项很是容易。 若是你正在使用具备多个参数的构造函数注入,那么咱们会认为该类违反单一责任原则。

自动装配依赖关系

链接容许Spring容器经过检查已定义的bean来自动解决协做bean之间的依赖关系。

使用XML配置有四种自动装配bean的模式:

  • no:默认值 - 这意味着没有为bean使用自动装配,咱们必须明确地命名依赖项
  • byName:自动装配是基于属性的名称完成的,所以Spring将查找与须要设置的属性同名的bean
  • byType:相似于byName自动装配,仅基于属性的类型。这意味着Spring将查找具备相同类型属性的bean。若是存在多个该类型的bean,则框架会抛出异常。
  • constructor:自动装配是基于构造函数参数完成的,这意味着Spring将查找与构造函数参数具备相同类型的bean
@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 id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>
复制代码

接下来,让咱们经过XML将名为item的bean注入到商店bean的item属性中:

<bean id="item" class="org.baeldung.store.ItemImpl1" />
 
<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>
复制代码

咱们还能够经过构造函数参数或setter显式定义依赖关系来覆盖自动装配。

beans 延迟初始化

默认状况下,容器在初始化期间建立并配置全部单例bean。 要避免这种状况,能够在bean配置中使用值为true的lazy-init属性:

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

所以,item1 bean只在首次请求时初始化,而不是在启动时初始化。这样作的好处是更快的初始化时间,可是带来的问题是,只有在请求bean以后才能发现配置错误,这多是应用程序运行后几个小时甚至几天。

相关文章
相关标签/搜索