4.更容易通过隔离组件或模拟其依赖项来测试程序,并允许组件通过契约进行通信
我们可以通过各种机制实现IoC,例如:策略设计模式、服务定位器模式、工厂模式和依赖注入(DI)。
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和DI都是简单的概念,但它们对我们构建系统的方式有深刻的影响,因此值得充分理解。
ApplicationContext context =newClassPathXmlApplicationContext("applicationContext.xml");在上面的示例中,我们可以使用元数据设置item属性,然后容器将读取此元数据并在运行时使用它来组装bean。在Spring中,可以通过构造函数、setter或字段来进行依赖注入。
@Configuration public class AppConfig { @Bean public Item item1() { return new ItemImpl1(); } @Bean public Store store() { return new Store(item1()); } }@Configuration注释表示该类是bean定义的源。我们也可以将其添加到多个配置类中。我们在方法上使用@Bean注释来定义bean。如果我们没有指定自定义名称,则bean名称将默认为方法名称。对于默认的singleton范围的bean,Spring首先检查是否已存在缓存的bean实例,仅在不存在时创建新实例。如果我们使用prototype范围,则容器为每个方法调用返回一个新的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的依赖注入
@Bean public Store store() { Store store =new Store(); store.setItem(item1()); return store; }我们也可以使用XML进行相同的bean配置:
<bean id="store" class="org.baeldung.store.Store"> <property name="item" ref="item1" /> </bean>我们可以将构造函数和setter类型的注入结合在同一个bean中。Spring文档建议将基于构造函数的注入用于必需的依赖项,将基于setter的注入用于可选的依赖项。
public class Store { @Autowired private Item item; }在构造Store对象时,如果没有构造函数或setter方法将Itembean注入其中,容器将使用反射将Item注入Store中。我们也可以使用XML来实现这一点。
使用此方法很容易添加多个依赖项。如果我们使用构造函数注入,有多个参数会让我们认为这个类做了不止一件事,这可能违反单一责任原则。
public class AppConfig { @Bean public Item item() { return new ItemImpl1(); } @Bean(autowire = Autowire.BY_TYPE) public Store store() { return new Store(); } }请注意,自Spring 5.1起,autowire属性已弃用。
public class Store { @Autowired private Item item; }如果存在相同类型的多个bean,则可以使用@Qualifier注释按名称引用bean:
public class Store { @Autowired @Qualifier("item1") private Item item; }现在,让我们通过XML配置按类型自动装配bean:
<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>接下来,让我们通过XML按名称将名为item的bean注入到store bean的item属性中:
<bean id="item" class="org.baeldung.store.ItemImpl1" /> <bean id="store" class="org.baeldung.store.Store" autowire="byName"> </bean>我们还可以通过构造函数参数或setter显式定义依赖关系来覆盖自动装配。
<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />因此,只有在第一次请求它时,才会初始化item1 bean,而不是在启动时。这样做的优点是初始化时间更快,但缺点是我们在bean被请求之后才会发现任何配置错误,这可能是应用程序已运行数小时甚至数天之后。