使用XML配置的Spring自动装配示例
在Spring中自动装配意味着Spring容器可以通过检查ApplicationContext的内容来自动解决bean之间的协作(bean依赖关系)。
在Spring中自动装配的方法
在Spring中自动布线有三个选项。
- 我们可以选择使用传统的基于XML的配置自动装配。
- 使用@Autowired注释自动布线。请参阅使用@Autowired注解的Spring自动装配后查看示例。
- 使用JSR 330的@Inject注释进行自动检测。请参阅使用@Inject和@Named注释的Spring自动装配后的示例。
在本文中,我们将看到使用XML配置的Spring自动装配示例。
Spring自动装配模式
Spring框架中有四种自动装配模式。
否–默认情况下,使用基于XML的配置时没有自动装配。 Bean引用必须由ref元素定义。
byName –在按属性名称自动装配中,Spring查找与需要自动装配的属性同名的bean。例如,如果一个bean包含一个名为item的属性(也就是说,它具有setItem()方法),Spring将查找一个名为item的bean定义,并使用它来设置该属性。
byType –在自动装配byType中,如果容器中恰好存在一个属性类型的bean,Spring会自动为其布线。如果存在多个,则会引发致命异常。如果没有匹配的bean,则什么都不会发生(未设置该属性)。
构造函数–构造函数自动装配与byType相似,但适用于构造函数参数。如果容器中不存在构造函数参数类型的一个bean,则将引发致命错误。
使用XML配置的Spring自动装配示例
我们将在Spring中看到自动装配的示例,其中使用XML配置了自动装配模式。在该示例中,有一个用于下订单的类称为OrderService,可以从商店进行购买。在OrderServiceImpl中,必须自动关联商店的类依赖关系。
使用byName自动装配
通过名称自动装配时,Spring容器将查找具有与XML相同的名称或者ID的bean,以自动装配。
public interface OrderService { public void buyItems(); }
public class OrderServiceImpl implements OrderService { private IStore store; public void setStore(IStore store) { this.store = store; } public void buyItems() { store.doPurchase(); } }
public interface IStore { public void doPurchase(); }
public class RetailStore implements IStore { public void doPurchase() { System.out.println("Doing purchase from Retail Store"); } }
XML配置(appContext.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.service.RetailStore" /> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" autowire="byName" /> </beans>
我们可以将以下类与main方法一起使用以读取配置并调用bean方法。
public class App { public static void main( String[] args ){ // create context using configuration ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml"); OrderService order = context.getBean("orderBean", OrderServiceImpl.class); order.buyItems(); // close the context context.close(); } }
输出:
16:47:38.923 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [appcontext.xml] 16:47:39.009 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'store' 16:47:39.050 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderBean' Doing purchase from Retail Store
使用byType自动装配
当按类型自动装配时,Spring容器会在XML配置中查找具有兼容类型的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"> <!-- Store bean --> <bean id="store" class="com.theitroad.springproject.service.RetailStore" /> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" autowire="byType" /> </beans>
通过类型自动装配时的冲突解决
当按类型自动装配时,在这种情况下,Spring容器将无法确定要自动装配哪个bean并抛出NoUniqueBeanDefinitionException。
例如,如果有两个商店的Istore类型为RetailStore和OnlineStore。
public class OnlineStore implements IStore { public void doPurchase() { System.out.println("Doing purchase from Online Store"); } }
两者都在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="retailStore" class="com.theitroad.springproject.service.RetailStore" /> <!-- Store bean --> <bean id="onlineStore" class="com.theitroad.springproject.service.OnlineStore" /> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" autowire="byType" /> </beans>
这导致NoUniqueBeanDefinitionException作为Spring容器,将IStore类型的bean自动装配。
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderBean' defined in class path resource [appcontext.xml]: Unsatisfied dependency expressed through bean property 'store'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.theitroad.springproject.service.IStore' available: expected single matching bean but found 2: retailStore,onlineStore
有两种方法可以解决由于具有相同类型的多个bean定义而引起的冲突
通过使用主要属性
通过使用限定词
使用主要解决冲突
通过使用primary属性,我们可以指示当多个bean是要自动装配到单值依赖项的候选对象时,应该为特定的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"> <!-- Store bean --> <bean id="retailStore" class="com.theitroad.springproject.service.RetailStore" primary="true"/> <!-- Store bean --> <bean id="onlineStore" class="com.theitroad.springproject.service.OnlineStore" /> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" autowire="byType" /> </beans>
这里的primary属性与RetailStore bean一起使用,因此在自动装配时会优先考虑此bean。
使用限定符解决冲突
Spring的Qualifier元素可更好地控制选择过程。我们可以将限定符值与特定的参数相关联,从而缩小类型匹配的范围,以便为每个参数选择特定的bean。 Qualifier旨在与自动自动装配一起使用。
<?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:annotation-config/> <!-- Store bean --> <bean id="retailStore" class="com.theitroad.springproject.service.RetailStore"> <qualifier value="rstore"/> </bean> <!-- Store bean --> <bean id="onlineStore" class="com.theitroad.springproject.service.OnlineStore"> <qualifier value="ostore"/> </bean> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" /> </beans>
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; public class OrderServiceImpl implements OrderService { @Autowired @Qualifier("ostore") private IStore store; public void buyItems() { store.doPurchase(); } }
使用构造器自动装配
通过构造器自动装配类似于通过类型自动装配,构造函数参数的构造函数类型用于搜索具有相同类型的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"> <!-- Store bean --> <bean id="retailStore" class="com.theitroad.springproject.service.RetailStore" /> <!-- OrderServiceImpl bean with store bean dependency --> <bean id="orderBean" class="com.theitroad.springproject.service.OrderServiceImpl" autowire="constructor" /> </beans>
public class OrderServiceImpl implements OrderService { private IStore store; public OrderServiceImpl(IStore store){ this.store = store; } public void buyItems() { store.doPurchase(); } }
输出:
18:46:38.298 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [appcontext.xml] 18:46:38.384 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'retailStore' 18:46:38.422 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderBean' 18:46:38.465 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderBean' via constructor to bean named 'retailStore' Doing purchase from Retail Store