欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

IoC控制反转、DI依赖注入、Spring框架实现,构造函数、set方法、字段反射

时间:2023-06-16

1、Overview 概述

In this tutorial, we'll introduce the concepts of IoC (Inversion of Control) and DI (Dependency Injection), as well as take a look at how these are implemented in the Spring framework.

在这篇文章中,我们来认识下IoC(控制反转)和DI(依赖注入)这两个概念,同时也来探究下在Spring框架中,是如何实现它们的。

2、What Is Inversion of Control? 什么是控制反转?

Inversion of Control is a principle in software engineering which transfers the control of objects or portions of a program to a container or framework、We most often use it in the context of object-oriented programming.

控制反转是转件工程中的一个概念,它把对象或部件的控制权限交由给一个容器或框架。这在面向对象的OOP编程中经常使用。

In contrast with traditional programming, in which our custom code makes calls to a library, IoC enables a framework to take control of the flow of a program and make calls to our custom code、To enable this, frameworks use abstractions with additional behavior built in. If we want to add our own behavior, we need to extend the classes of the framework or plugin our own classes.

和传统的编程模式(编码主动的调用一个个库,强关联,主动找寻依赖的库)相比,控制反转可以让第三方框架来控制程序流程,并在合适的时机调用我们的代码来执行。为了实现这个目标,第三方框架一般使用有附加行为的抽象概念层。如果我们要增加自己的行为,我们只需要扩展这些抽象概念类,或者在框架中植入我们的类文件。

The advantages of this architecture are:

decoupling the execution of a task from its implementationmaking it easier to switch between different implementationsgreater modularity of a programgreater ease in testing a program by isolating a component or mocking its dependencies, and allowing components to communicate through contracts

使用容器或框架来控制程序运行,这种架构的好处有以下几点:

把一个任务的执行调用与其实现代码解耦,即不进行硬编码调用,让框架决定何时执行可以轻易的在不同的实现类进行切换,如MySQL换成Oracle,替换jar包及DAO层即可程序更加的模块化,实现低耦合可以用过隔离组件或模拟它的依赖来进行程序测试,也允许组件之间通过接口来通信

We can achieve Inversion of Control through various mechanisms such as: Strategy design pattern, Service Locator pattern, Factory pattern, and Dependency Injection (DI).

我们可以使用多种多样不同的架构来实现控制反转,例如:策略模式,服务发现模式,工厂模式和依赖注入模式。

3、What Is Dependency Injection? 什么是依赖注入?

Dependency injection is a pattern we can use to implement IoC, where the control being inverted is setting an object's dependencies.

依赖注入是一种可以实现控制反转的设计模式,通过框架(框架有设置对象依赖的控制权,通过注入到setter、constructor等)来设置对象的依赖。

Connecting objects with other objects, or “injecting” objects into other objects, is done by an assembler rather than by the objects themselves.

链接对象,或者说把对象依赖的其它对象注入,是通过框架中的组装器来实现,而不是通过对象硬编码来实现调用的。

Here's how we would create an object dependency in traditional programming:

下面是在传统编程中,如何实现依赖对象的创建工作的:

public class Store { private Item item; public Store() { item = new ItemImpl1(); }}

In the example above, we need to instantiate an implementation of the Item interface within the Store class itself.

在上面的代码示例中,我们需要在Store类代码中硬编码实例化Item接口的实现类ItemImpl1。

By using DI, we can rewrite the example without specifying the implementation of the Item that we want:

而通过依赖注入,我们可以实现上述功能,而且不用去硬编码指定Item的某个具体实现类。

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

In the next sections, we'll look at how we can provide the implementation of Item through metadata.

在下一节中,我们一直学习,如何通过无数据给Item指定它的实现类。

Both IoC and DI are simple concepts, but they have deep implications in the way we structure our systems, so they're well worth understanding fully.

尽管控制反转IoC和依赖注入DI很简单,但是在构造我们的系统框架时有深切的意义,所以它们值得我们花时间去好好的了解。

4、The Spring IoC Container Spring的IoC容器

An IoC container is a common characteristic of frameworks that implement IoC.

IoC容器是那些实现的IoC概念框架的一个通用解决方案,一个共通的特点。

In the Spring framework, the interface ApplicationContext represents the IoC container、The Spring container is responsible for instantiating, configuring and assembling objects known as beans, as well as managing their life cycles.

在Spring框架中,IoC容器的实现就是ApplicationContext接口。它负责实例化、配置和组装对象,也负责管理它们的生命周期,这些对象就是我们熟知的bean。

The Spring framework provides several implementations of the ApplicationContext interface: ClassPathXmlApplicationContext and FileSystemXmlApplicationContext for standalone applications, and WebApplicationContext for web applications.

Spring框架提供了ApplicationContext接口的几个实现子类,如为单体程序提供的ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,还有为Web应用程序提供的WebApplicationContext。

In order to assemble beans, the container uses configuration metadata, which can be in the form of XML configuration or annotations.

而为也组装关联这个bean,Spring容器使用配置元信息实现,这些配置可以是xml类型,也可以通过注解的方式来实现。

Here's one way to manually instantiate a container:

下面是手动创建一个Spring容器的示例代码:

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

To set the item attribute in the example above, we can use metadata、Then the container will read this metadata and use it to assemble beans at runtime.

为了给上一节代码中的Item设置它的实现类,我们可以元信息来配置,然后容器就会读取配置并在运行时来组合它所需要的依赖。

Dependency Injection in Spring can be done through constructors, setters or fields.

在Spring中,依赖注入可以通过构造函数,set方法和字段设置来实现。

5、Constructor-based Dependency Injection 基于构造函数依赖注入

In the case of constructor-based dependency injection, the container will invoke a constructor with arguments each representing a dependency we want to set.

在通过构造函数进行依赖注入的场景中,容器会把程序运行中我们想要设置的具体实现类(代码所需的依赖类)拼装成参数,并执行对象的构造函数来设置。

Spring resolves each argument primarily by type, followed by name of the attribute, and index for disambiguation、Let's see the configuration of a bean and its dependencies using annotations:

Spring框架主要是通过参数类型来解析传入的参数,其次是通过参数的名称,最后通过参数的索引序号来指定。下面让我们来看一下如何使用注解来配置对象bean以及它所需的依赖吧

@Configurationpublic class AppConfig { @Bean public Item item1() { return new ItemImpl1(); } @Bean public Store store() { return new Store(item1()); }}

The @Configuration annotation indicates that the class is a source of bean definitions、We can also add it to multiple configuration classes.

这个@Configuration注解意味着这个类是用来定义bean的特有类,可以标记多个类为bean定义类。

We use the @Bean annotation on a method to define a bean、If we don't specify a custom name, then the bean name will default to the method name.

在方法上使用@Bean注解可以定义一个bean对象。如果我们不指定一个对象名,那么方法名会被默认当作这个对象的实例名,被其它资源引用。

For a bean with the default singleton scope, Spring first checks if a cached instance of the bean already exists, and only creates a new one if it doesn't、If we're using the prototype scope, the container returns a new bean instance for each method call.

在Spring中,对象bean默认是以单例模式使用的,Spring框架会首先检查实例是否已经存在于缓存之中,没有存在(没创建)时都会新建一个实例对象。如果设置为原型模式,那么Spring框架则会在返回实例之前调用这个方法来创建对象。

Another way to create the configuration of the beans is through XML configuration:

另外一种方式是通过XML文件来配置bean对象,示例如下:

6、Setter-based Dependency Injection 基于SET方法的依赖注入

For setter-based DI, the container will call setter methods of our class after invoking a no-argument constructor or no-argument static factory method to instantiate the bean、Let's create this configuration using annotations:

对于基于SET方法的依赖注入,容器会在调用无参的构造函数或无参的静态工厂方法实例bean对象后,调用set方法来来设置依赖。下面通过代码使用注解来创建一个依赖配置

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

We can also use XML for the same configuration of beans:

同样,也可以通过XML文件来配置依赖

We can combine constructor-based and setter-based types of injection for the same bean、The Spring documentation recommends using constructor-based injection for mandatory dependencies, and setter-based injection for optional ones.

可以在同一个bean对象上同时使用构造函数和set方法进行依赖注入,在Spring文档中,推荐使用构造函数注入那个强制依赖,通过set方法注入可选依赖。

7、Field-based Dependency Injection 基于字段的依赖注入

In case of Field-based DI, we can inject the dependencies by marking them with an @Autowiredannotation:

在基于字段进行依赖注入的场景中,我们可以使用@Autowired注解来标记,框架自动识别注入

public class Store { @Autowired private Item item; }

While constructing the Store object, if there's no constructor or setter method to inject the Item bean, the container will use reflection to inject Item into Store.

在框架创建Store对象的时候,如果没有构造函数和set方法的注入形式存在,容器会通过反射的方式为它注入Item依赖。

We can also achieve this using XML configuration.

也同样可以使用XML来实现这个配置。

This approach might look simpler and cleaner, but we don't recommend using it because it has a few drawbacks such as:

This method uses reflection to inject the dependencies, which is costlier than constructor-based or setter-based injection.It's really easy to keep adding multiple dependencies using this approach、If we were using constructor injection, having multiple arguments would make us think that the class does more than one thing, which can violate the Single Responsibility Principle.

基于字段的依赖注入可能看似更简洁明了,但是并不推荐使用,因为下面几个缺点:

这种方式使用反射来注入所需的依赖,比构造函数和set方法的注入需要更多资源、时间使用这种方式,让人在不知不觉中为这个类添加更多的依赖,使其可以完成更多的功能,这有些违背单一职能原则。而使用构造函数形式来注入依赖,出现在构造函数中的冗长参数,会让人情不自禁的想到这点。

8、Autowiring Dependencies 自动织入依赖

Wiring allows the Spring container to automatically resolve dependencies between collaborating beans by inspecting the beans that have been defined.

织入功能让Spring容器可以通过检查已经定义的bean对象来自动的解析bean对象之间的相互依赖。

There are four modes of autowiring a bean using an XML configuration:

no: the default value – this means no autowiring is used for the bean and we have to explicitly name the dependencies.byName: autowiring is done based on the name of the property, therefore Spring will look for a bean with the same name as the property that needs to be set.byType: similar to the byName autowiring, only based on the type of the property、This means Spring will look for a bean with the same type of the property to set、If there's more than one bean of that type, the framework throws an exception.constructor: autowiring is done based on constructor arguments, meaning Spring will look for beans with the same type as the constructor arguments.

XML有四种配置依赖的方式:

no:默认配置方式,这意味着我们需要清晰的指定bean对象之间的依赖,自动依赖织入将不起作用byName:通过属性名称来解析注入依赖,这种方式下,Spring框架会在容器中找寻指定名称的bean对象来完成依赖注入byType:和名称注入类似,只不过它是基于属性的类型。这意味着Spring框架会在容器中寻找与属性相同类型的bean对象来注入依赖。但是,如果找到多于一个依赖对象,会报异常constructor:通过构造函数的形式来进行依赖注入,意思是说,Spring框架会解析bean对象的构造函数,并根据所需要的参数类型在容器中找到对应类型的bean对象,完成依赖注入。

For example, let's autowire the item1 bean defined above by type into the store bean:

例如,让我们通过byType的形式来把上面定义的item1对象注入到store对象中,示例如下

@Bean(autowire = Autowire.BY_TYPE)public class Store { private Item item; public setItem(Item item){ this.item = item; }}

We can also inject beans using the @Autowired annotation for autowiring by type:

也可以通过@Autowired注解的方式来指定通过byType方式来注入依赖bean:

public class Store { @Autowired private Item item;}

If there's more than one bean of the same type, we can use the @Qualifier annotation to reference a bean by name:

如果存在不止一个且类型相同的bean对象,可以使用@Qualifier注解来指定要使用的依赖对象

public class Store { @Autowired @Qualifier("item1") private Item item;}

Now let's autowire beans by type through XML configuration:

同样,可以使用XML文件来指定byType模式的依赖注入

Next, let's inject a bean named item into the item property of store bean by name through XML:

使用byName方式的依赖注入,和byType方式类似,示例如下

We can also override the autowiring by defining dependencies explicitly through constructor arguments or setters.

使用构造函数模式、set方法的依赖注入模式,和上面的大同小异。

9、Lazy Initialized Beans 延迟实例化bean对象

By default, the container creates and configures all singleton beans during initialization、To avoid this, we can use the lazy-init attribute with value true on the bean configuration:

默认情况下,容器会在初始化时创建并配置所有单例对象及其所需的依赖,当然了,这是相当耗时的。为了避免这种状况,可以在bean的配置项上加入lazy-init属性,并设置值为true即可。

Consequently, the item1 bean will only be initialized when it's first requested, and not at startup、The advantage of this is faster initialization time, but the trade-off is that we won't discover any configuration errors until after the bean is requested, which could be several hours or even days after the application has already been running.

上面配置的结果就是,item1对象不会在启动阶段被初始化,只会在首次使用时进行初始化。这样做的优点是,初始化阶段可以节省不少时间,但是它的代价是,我们将不能发现任何配置错误,走到这些bean对象被使用。而这些错误出现的时机,有可能在程序已经运行了几小时,甚至是几天之后才出现。

10、Conclusion 总结

In this article, we presented the concepts of Inversion of Control and Dependency Injection, and exemplified them in the Spring framework.

这篇文章中,我们展现了控制反转IoC及依赖注入DI的概念,以及阐述了Spring框架中的实现。

多多点赞关注哦

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。