静下心来的个人专栏
上一篇

什么是控制反转(IOC)

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

什么是控制反转(IOC)

控制反转 (IoC) 和依赖注入 (DI) 是用于解耦类依赖关系的编程模式。在本文中,我们将讨论 Spring Framework 的 IoC 和 DI。

为简单起见,假设类 Vehicle 依赖于类 Engine,这意味着没有 EngineVehicle 没有意义。我们需要确保通过保持代码松散耦合来满足这种依赖关系。所以我们使用一个特殊的类来确保对象的创建以正确的顺序完成并且依赖关系得到照顾。这个特殊的类有时被称为容器或工厂。

控制反转是一种通过将对象创建转移到容器或框架来确保解耦的技术。该框架还允许我们根据开发人员的需要自定义 Objection 创建并添加自定义行为。之所以称为反转,是因为该过程是反向的,其中 bean 创建和依赖由容器而不是 bean 本身控制。

IOC的优势:

1、解耦对象创建过程及其执行过程。

2、有助于运行时多态性。

3、更好的代码可维护性。

4、由于接口和实现之间的松散耦合,易于代码测试。

实现 IoC 的机制:

IoC 可以通过各种编程技术和模式来实现,例如策略设计模式、服务定位器模式、工厂模式和依赖注入。

 

上一节我们已经用代码介绍了依赖注入,今天我们用另一个例子来说明:

传统的耦合代码:

//Traditional tightly coupled code.
public class Store {
  private Item item;
  public Store() {
    this.item = new Item();
  }
}

依赖注入代码:

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

注意 – 我们需要通过元数据配置 IoC 容器,以便容器拥有创建 Bean 所需的信息。

接下来,我们将探索 Spring Framework IOC Container。让我们开始实际的 Spring 编码。

Spring 框架 IoC 容器

控制反转容器是 Spring Framework 的核心。 Spring 生态系统中的所有其他库/模块都建立在此之上。要使用 Spring IoC,请确保 pom.xml 中有 spring-context 依赖项。

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.2.RELEASE</version>
</dependency>

在 Spring 框架中

1、ApplicationContext 接口代表 Spring IoC 容器。

2、相同的接口还负责 Bean 的实例化、配置和管理。

3、bean 配置元数据可以以 XML、Java 注释或 Java 代码的形式提供。

4、Spring 提供了几种 ApplicationContext 接口的实现。例如,要在独立应用程序中使用基于 XML 的元数据配置,它具有 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext。同样,如果您在基于独立注释的配置中工作,则使用 AnnotationConfigApplicationContext。

Spring 中的元数据可以通过 3 种方式进行配置

1、使用基于纯 XML 的配置

2、使用基于注释的配置。这种方法利用注释和基于 XML 的配置。

3100% 基于 Java 的配置。这种方法还利用了注释。

 

Spring中依赖注入的三种方式

请记住,Spring 中的依赖注入可以通过三种方式完成,通过构造函数、设置器或字段。我们将使用基于 Java 的配置以及基于 XML 的配置来学习这些 DI 方法中的每一个。

基于构造函数的依赖注入

在基于构造函数的 DI 中,IoC 容器将调用构造函数来注入其必要的依赖项。元数据配置可以在 java 配置文件或 XML bean-config 文件中提供。

用于构造函数注入的基于 XML 的元数据配置

下面的代码显示了使用基于 XML 的配置来执行基于构造函数的依赖注入。您不必接触 java POJO 类。所有配置都在 bean-config.xml 文件中完成。

Spring 中的 bean 的默认作用域为 Singleton。由于我们希望每次要求 spring 返回一个新 bean 时都创建一个新实例,因此将 bean 范围指定为原型范围 =“prototype”。如您在 bean-config.xml 文件中所见,构造函数 DI 是使用构造函数参数完成的。

正如您在 TestXMLConstructorInjection 类中看到的那样,ApplicationContext 实例是使用 ClassPathXmlApplicationContext 类创建的。

Item.java

package basic.ioc.constructor.xml;
import java.util.StringJoiner;
public class Item {
  private Long id;
  private String name;
  public Item() {
  }
  public Item(Long id, String name) {
    this.id = id;
    this.name = name;
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  @Override
  public String toString() {
    return new StringJoiner(", ", Item.class.getSimpleName() + "[", "]")
        .add("id=" + id)
        .add("name='" + name + "'")
        .toString();
  }
}

Store.java

package basic.ioc.constructor.xml;
import java.util.StringJoiner;
public class Store {
  private String id;
  private Item item;
  public Store() {
  }
  public Store(String id, Item item) {
    this.id = id;
    this.item = item;
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public Item getItem() {
    return item;
  }
  public void setItem(Item item) {
    this.item = item;
  }
  @Override
  public String toString() {
    return new StringJoiner(", ", Store.class.getSimpleName() + "[", "]")
        .add("id=" + id)
        .add("item=" + item)
        .toString();
  }
}

bean-config.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 id="item" scope="prototype" class="basic.ioc.constructor.xml.Item"/>
  <bean id="store" scope="prototype" class="basic.ioc.constructor.xml.Store">
    <constructor-arg type="java.lang.String" value="#{ T(java.util.UUID).randomUUID().toString() }"/>
    <constructor-arg type="basic.ioc.constructor.xml.Item" ref="item"/>
  </bean>
</beans>

TestXMLConstructorInjection.java

package basic.ioc.constructor.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestXMLConstructorInjection {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean-config.xml");
    System.out.println(context.getBean(Store.class));
  }
}

输出:

Store[id=ad25498e-9538-422f-99a4-eb65d112d6fd, item=Item[id=null, name='null']]

用于构造函数注入的基于 Java 的元数据配置

就像 XML bean 配置文件一样,我们创建一个 java 类 BasicIocConfig 用于保存元数据配置。在实际场景中,您可以拥有多个这样的配置类。

1、@Configuration 注解表明该类是一个 bean 定义配置文件。

2、@Bean 注解指示 bean 名称,用于创建 bean 的方法。

3、默认的 bean 作用域是单例的,为了得到原型作用域,所以我们使用了@Scope 注解。

4、最后,AnnotationConfigApplicationContext 实现类用于创建ApplicationContext 的实例。

BasicIocConfig.java

package basic.ioc.constructor.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import java.util.UUID;
@Configuration
public class BasicIocConfig {
  @Bean
  @Scope("prototype")
  public Item item() {
    return new Item();
  }
  @Bean
  @Scope("prototype")
  public Store store() {
    return new Store(
        UUID.randomUUID().toString(), this.item()
    );
  }
}

TestInjection.java

package basic.ioc.constructor.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestInjection {
  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(BasicIocConfig.class);
    Store store = context.getBean(Store.class);
    System.out.println(store);
  }
}

Store.java 和 Item.java与上面一致。

输出如下:

Store[id=c960066d-5142-49f5-83bd-1d53012449ef, item=Item[id=null, name='null']]

注意:建议使用带有注释的基于 Java 的配置,而不是基于 XML 的配置。

还有另外两种注入方式以后再介绍。

版权声明:著作权归作者所有。