使用XML配置的Spring自动装配示例

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

在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