什么是控制反转(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 的配置。

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

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

相关推荐

Android dex文件反编译为Java源码

工具准备 dex2jar:国人写的一个dex反编译为java的工具jd-gui:查看java源码的gui工具反编译步骤步骤一把test_apk-debug.apk里的classes.dex转换为test_apk-debug_dex2jar.jard2j-dex2jar.sh -f -o output_jar.jar apk_to_decompile.apk

Kotlin:流程控制之条件语句 if 和 when

在Kotlin有两种用于判断条件的语句if 和when。if在Kotlin,if既可以是条件语句,仅仅用作条件判断,还可以是条件表达式,表达式会返回一个值。传统的条件判断if语句var max = a  if (a < b) max = b if-elsevar max:&

Java 10 var的使用及限制

在Java 10的众多特性里,局部变量的类型推断是比较受大家关注的特性之一。这里简单介绍下它的使用以及限制。在Java 7,声明一个列表我们会这样做:List<String> list = new ArrayList<String>(); Java 8/9可以改写为:List<String>&

iOS音量控制相关代码

隐藏系统的音量控件隐藏系统的音量控件的办法是,从iOS系统获取音量控件,然后让它不可见,代码如下:import UIKit import MediaPlayer class ViewController: UIViewController {   override func viewDidLoad()&n

Tensorflow 设置CUDA_VISIBLE_DEVICES来控制GPU的使用

如果服务器有多个GPU,tensorflow默认会全部使用。如果只想使用部分GPU,可以通过参数CUDA_VISIBLE_DEVICES来设置GPU的可见性。示例:Environment Variable Syntax ResultsCUDA_VISIBLE_DEVICES=1 Only device 1 will be seenCUDA_VISIBLE_DEVICE

Spirng Security使用注解对方法做权限安全控制

 Spring Security默认情况下是关闭了对方法级的安全控制。可以通过xml或者是在添加了@Configuration注解的bean上添加@EnableGlobalMethodSecurity来开启对方法级别的安全控制。Spring Security 支持三种方法级注解, 分别是 JSR-205/Secured 注解/prePostEnabled,可以在开启对方法级的安全控制时设

如何对REST API进行版本控制

如果您对API不太熟悉,您可能会想...为什么对API版本控制大惊小怪?如果您对API的更改感到厌倦,那么您可能会大惊小怪。如果您是API的维护者,那么您可能还会大惊小怪地尝试解决诸如此类的难题:# 下面的v2(版本2)表示的只是产品版本还是整个APId呢?/v2/products# 是什么促使v1和v2的更改呢? 它们之间有什么不同呢?/v1/products/v2/products这些有关版本

C#反序列化解析json为动态对象

C#反序列化解析json为动态对象两种方法。方法一:System.Web.Helpers.Json最简洁的方式使用System.Web.Helpers.Json ,它的Decode方法返回一个动态对象,我们可以根据需要遍历它。使用很简洁:var dynamicObject = Json.Decode(jsonString);System.Web.Helpers.dll 依赖于.NET 4