Spring 基础知识之IOC介绍

栏目: Java · 发布时间: 5年前

内容简介:在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,然后我们将看看它们是如何在Spring框架中实现的。控制反转是软件工程中的一个原则,通过该原理,对象或程序的一部分的控制被转移到容器或框架。 它最常用于面向对象编程的上下文中。与我们的自定义代码调用库的传统编程相比,IoC使框架能够控制程序流并调用我们的自定义代码。 为了实现这一点,框架使用内置额外行为的抽象。如果我们想要添加自己的行为,我们需要扩展框架的类或插入我们自己的类。

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

什么是控制反转

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

与我们的自定义代码调用库的传统编程相比,IoC使框架能够控制程序流并调用我们的自定义代码。 为了实现这一点,框架使用内置额外行为的抽象。如果我们想要添加自己的行为,我们需要扩展框架的类或插入我们自己的类。

这种架构的优点是:

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

控制反转可以通过各种机制实现,例如:策略设计模式,服务定位模式,工厂模式和依赖注入(DI)。

什么是依赖注入

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

将对象与其他对象连接或将对象“注入”其他对象的行为由汇编程序而不是对象本身完成。

以下是在传统编程中创建对象依赖项的方法:

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之后才能发现配置错误,这可能是应用程序运行后几个小时甚至几天。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SRE

SRE

贝特西 拜尔 (Betsy Beyer)、等 / 孙宇聪 / 电子工业出版社 / 2016-10-1 / CNY 108.00

大型软件系统生命周期的绝大部分都处于“使用”阶段,而非“设计”或“实现”阶段。那么为什么我们却总是认为软件工程应该首要关注设计和实现呢?在《SRE:Google运维解密》中,Google SRE的关键成员解释了他们是如何对软件进行生命周期的整体性关注的,以及为什么这样做能够帮助Google成功地构建、部署、监控和运维世界上现存最大的软件系统。通过阅读《SRE:Google运维解密》,读者可以学习到......一起来看看 《SRE》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码