Spring中的构造函数依赖注入
在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注入主要应仅用于可以在类中分配合理的默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。"