Spring中的构造函数依赖注入

时间:2020-01-09 10:44:28  来源:igfitidea点击:

在Spring的依赖注入后,我们已经介绍了依赖注入的概念,在这篇文章中,我们将详细介绍Spring中的一种依赖注入构造函数依赖注入。

Spring构造函数依赖注入

在基于构造函数的DI中,Spring容器调用带有多个参数的类的构造函数,每个参数表示一个依赖项。为了配置基于构造函数的依赖关系,我们可以使用XML配置以及注释。我们将看到使用这两种方式进行操作的示例。

Spring构造函数依赖注入示例

在该示例中,有一个用于下达订单的类,称为Order,可以从在线商店或者零售商店进行购买。在Order类中,必须注入构造函数参数的依赖项。

public interface IStore {
  public void doPurchase(int items);
}
public class OnlineStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing online purchase of " + items + " Items");
  }
}
public class RetailStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing purchase of " + items + " Items from a brick and mortar store");
  }
}
public class Order {
  private IStore store;
  private int items;
  public Order(IStore store, int items) {
    this.store = store;
    this.items = items;
  }
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase(items);
  }
}

如果我们使用的是XML配置,那么将按照以下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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
  <!-- Store bean --> 
  <bean id="store" class="com.theitroad.SpringProject.OnlineStore" />           
    <!-- Order bean with dependencies -->
    <bean id="order" class="com.theitroad.SpringProject.Order">
    <constructor-arg ref="store" />
    <constructor-arg type="int" value="20" />
  </bean>
</beans>

为了提供构造函数依赖项,使用了<constructor-arg>标记。当依赖关系是针对另一个bean时,将使用" ref"属性,对于任何原始类型,将使用" value"属性。

我们可以将以下类与main方法一起使用以读取配置并调用bean方法。

public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
    Order order = (Order) context.getBean("order");
    order.buyItems();
    // close the context
    context.close();
  }
}

如果要使用注释在Spring中配置构造函数依赖项,则必须将@Service或者@Component注释与这些类一起使用,以表明它们是Spring托管的组件,并且在完成组件扫描时会自动发现它们。

@Service
public class OnlineStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing online purchase of " + items + " Items");
  }
}
@Service
public class RetailStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing purchase of " + items + " Items from a brick and mortar store");
  }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class Order {
  private IStore store;
  @Value("20")
  private int items;
  @Autowired	
  public Order(@Qualifier("retailStore")IStore store) {
    this.store = store;
  }
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase(items);
  }
}

构造函数上的@Autowired注释表示必须自动关联依赖项。由于存在两个类型为store的对象,因此已使用@Qualifier注释来告知必须连接哪个bean,否则将出现错误"没有可用的com.theitroad.SpringProject.IStore类型的合格bean"。

自动装配仅适用于引用,因此使用@Value注释提供原始值。

如果要使用XML设置组件扫描以自动发现Bean,则可以使用以下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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="com.theitroad.SpringProject" />
</beans>

XML配置现在没有任何依赖性,只有<context:component-scan>标记。

构造函数参数解析

对于构造函数依赖性,在bean定义中定义构造函数参数的顺序是在实例化Bean时将这些参数提供给适当的构造函数的顺序。

Bean定义的构造函数参数中可能存在歧义,这可能是因为有多个相同类型的Bean(通过继承关系的类)。我们已经看到了如何使用@Qualifier注释解决这种歧义。

对于基本类型,Spring无法确定值的类型,因此在没有帮助的情况下无法按类型进行匹配。在这种情况下,我们将必须使用"类型"属性或者"索引"属性。

构造函数参数类型匹配
在类型匹配中,使用type属性显式指定构造函数参数的类型。例如:

<bean id="orderBean" class="com.theitroad.SpringProject.Order">
  <constructor-arg type="int" value="20000" />
  <constructor-arg type="java.lang.String" value="20"/>
 </bean>

构造函数参数索引
使用index属性来显式指定构造函数参数的索引。请注意,索引基于0。

<bean id="orderBean" class="com.theitroad.SpringProject.Order">
	<constructor-arg index="0" value="20000" />
	<constructor-arg index="1" value="20"/>
</bean>

基于构造函数或者基于setter的依赖项注入

根据Spring文档,基于构造器的DI优于基于设置器的DI。

"由于可以混合使用基于构造函数的DI和基于setter的DI,因此,将构造函数用于强制性依赖项,将setter方法或者配置方法用于可选性依赖项是一个很好的经验法则。

Spring团队通常提倡构造函数注入,因为它可以使应用程序组件实现为不可变对象,并确保所需的依赖项不为null。此外,注入构造函数的组件总是以完全初始化的状态返回到客户端(调用)代码。

Setter注入主要应仅用于可以在类中分配合理的默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。"