Spring依赖注入示例
这篇文章展示了Spring框架中的依赖注入,以及使用XML配置和注解注入依赖的示例。
什么是依赖注入
如果我们甚至创建一个简单的应用程序,该应用程序将只有很少的对象相互交互以实现所需的功能。通常,在Java应用程序中,我们可以使用new运算符来创建类中所需的对象。例如在A类中,如果需要B类的对象,则可以按以下方式创建它:
class A{ Private B obj; public A(){ obj = new B(); } ... ... }
通过以这种方式提供依赖关系,类可以
紧密耦合,难以更换物体
测试不容易–显式依赖使模拟对象变得困难,从而使单元测试变得困难。
理想情况下,我们希望将类彼此分离,以使其易于重用,并且还易于模拟依赖关系以进行单元测试。
那就是依赖注入的作用,而不是由类本身创建或者查找将这些对象依赖注入到类中的其他对象(依赖)。从本质上讲,此过程是bean本身的逆过程,它自己控制依赖项的实例化或者位置,因此依赖项注入也被视为控制反转(IoC)的子类型之一。
Spring依赖注入
依赖注入是Spring框架的核心概念之一,它通过提供标准的方法来提供配置元数据,从而使DI变得容易,该配置元数据随后由Spring容器用于实例化对象和关联依赖关系。
Spring中的依赖注入存在两个主要变体:
基于构造函数的依赖注入
基于Setter的依赖注入
要配置依赖项,我们可以使用XML配置或者基于注释的配置,我们将看到这两种方式的示例。
具有XML配置的Spring依赖项注入示例
在该示例中,有一个用于下达订单的类,称为Order,可以从在线商店或者零售商店进行购买。在Order类中,必须注入商店的依赖项。
Spring框架建议依赖项应位于接口或者抽象基类上,以便可以在单元测试中轻松使用存根或者模拟实现,因此我们将对接口进行编码。
public interface IStore { public void doPurchase(); }
public class OnlineStore implements IStore { public void doPurchase() { System.out.println("Doing online purchase of Items"); } }
public class RetailStore implements IStore { public void doPurchase() { System.out.println("Doing purchase of Items from a brick and mortar store"); } }
public class Order { private IStore store; // Constructor dependency Order(IStore store){ this.store = store; } public void buyItems() { store.doPurchase(); } }
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- Store bean --> <bean id="store" class="com.theitroad.SpringProject.OnlineStore" /> <!-- Order bean with dependencies --> <bean id="orderBean" class="com.theitroad.SpringProject.Order"> <constructor-arg ref="store" /> </bean> </beans>
在XML配置中,为OnlineStore和Order类提供了bean定义。在orderBean中,存储的引用作为构造函数参数传递。
我们可以将以下类与main方法一起使用以读取配置并调用bean方法。
import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main( String[] args ){ // create context using configuration ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml"); Order order = (Order) context.getBean("orderBean"); order.buyItems(); // close the context context.close(); } }
如果要使用setter依赖项注入,那么Order类应具有该属性的setter方法。
public class Order { private IStore store; public void setStore(IStore store) { this.store = store; } public void buyItems() { store.doPurchase(); } }
还需要更改XML配置以获取setter依赖项。现在,属性元素用于定义依赖关系。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- Store bean --> <bean id="store" class="com.theitroad.SpringProject.RetailStore" /> <!-- Order bean with dependencies --> <bean id="orderBean" class="com.theitroad.SpringProject.Order"> <property name="store" ref="store" /> </bean> </beans>
带有注释的Spring依赖项注入示例
XML配置不是配置依赖项的唯一方法,我们可以使用@ Componenent,@ Service等注释来注释依赖项,而@Autowired则表示必须连接依赖项。
为了表明必须在XML中完成对bean的自动发现,我们只需要定义context:component-scan和base-package即可。在基本程序包中,我们可以传递类所在的程序包,以便自动扫描这些类。我们可以完全放弃XML并改用Java配置。
import org.springframework.stereotype.Service; @Service public class OnlineStore implements IStore { public void doPurchase() { System.out.println("Doing online purchase of Items"); } }
import org.springframework.stereotype.Service; @Service public class RetailStore implements IStore { public void doPurchase() { System.out.println("Doing purchase of Items from a brick and mortar store"); } }
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class Order { private IStore store; @Autowired @Qualifier("retailStore") public void setStore(IStore store) { this.store = store; } public void buyItems() { store.doPurchase(); } }
现在,这些类将使用@Service注释进行注释,该注释表示这些是Spring托管的组件,并且在完成组件扫描时会自动发现它们。
setter方法上的@Autowired注释表示必须自动关联依赖项。由于存在两个类型为store的对象,因此已使用@Qualifier注释来告知必须连接哪个bean,否则将出现错误"没有可用的com.theitroad.SpringProject.IStore类型的合格bean"。
现在,XML配置将不再具有任何依赖性,只有<context:component-scan>标记指示必须使用组件扫描来自动发现bean。
<?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>
我们可以将以下类与main方法一起使用以读取配置并调用bean方法。
import org.springframework.context.support.ClassPathXmlApplicationContext; 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(); } }