在本文中,咱们将介绍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的实现。
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的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。
这种方法可能看起来更简单,更清晰,但不推荐使用,由于它有一些缺点,例如:
链接容许Spring容器经过检查已定义的bean来自动解决协做bean之间的依赖关系。
使用XML配置有四种自动装配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显式定义依赖关系来覆盖自动装配。
默认状况下,容器在初始化期间建立并配置全部单例bean。 要避免这种状况,能够在bean配置中使用值为true的lazy-init属性:
<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />
复制代码
所以,item1 bean只在首次请求时初始化,而不是在启动时初始化。这样作的好处是更快的初始化时间,可是带来的问题是,只有在请求bean以后才能发现配置错误,这多是应用程序运行后几个小时甚至几天。