使用@Autowired注解进行Spring自动装配

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

在Spring框架中,我们提供配置数据,使用这些数据,Spring容器可以实例化bean和注入依赖项。在Spring中自动装配意味着Spring容器可以通过检查ApplicationContext的内容来自动解决bean之间的协作(bean依赖关系)。

Spring自动装配模式

Spring框架中有四种自动装配模式。

  • 否–默认情况下,使用基于XML的配置时没有自动装配。 Bean引用必须由ref元素定义。

  • byName –在按属性名称自动装配中,Spring查找与需要自动装配的属性同名的bean。例如,如果一个bean包含item属性(即,它具有setItem()方法),Spring将查找一个名为item的bean定义,并使用它来设置该属性。

  • byType –在自动装配byType中,如果容器中恰好存在一个该属性类型的bean,Spring会自动对属性进行布线。如果存在多个,则会引发致命异常。如果没有匹配的bean,则什么都不会发生(未设置该属性)。

  • 构造函数–构造函数自动装配与byType相似,但适用于构造函数参数。如果容器中不存在构造函数参数类型的一个bean,则将引发致命错误。

Spring自动装配

在Spring中自动装配有三个选项。

  • 我们可以选择使用传统的基于XML的配置自动装配。例如,请参见使用XML配置的Spring自动装配示例。

  • 使用@Autowired注释自动装配。

  • 使用JSR 330的@Inject注释进行自动检测。例如,请参阅使用@Inject和@Named注释进行Spring自动装配。

在这篇文章中,我们将看到使用@Autowired注解的Spring自动装配示例。

启用@Autowired注释

1.我们可以通过注册" AutowiredAnnotationBeanPostProcessor"类在Spring中使用@Autowired注释启用自动装配。

<bean class = "org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

2.通过在基于XML的Spring配置中使用<context:annotation-config />元素。
<context:annotation-config />元素隐式注册后处理器。隐式注册的后处理器包括AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor和前述的RequiredAnnotationBeanPostProcessor。
3.通过在基于XML的Spring配置中使用<context:component-scan>元素。 <context:component-scan>的使用隐式启用<context:annotation-config>元素的功能。大多数情况下,我们将使用此元素。

使用@Autowired注释

  • 我们可以将@Autowired注释应用于构造函数。

  • 我们可以将@Autowired注释应用到setter方法。

  • 我们可以将@Autowired应用于字段。

  • 我们还可以将注释应用于具有任意名称和多个参数的方法。

Spring @Autowired注释示例

我们将使用上述所有选项查看@Autowired的示例。在该示例中,有一个用于下订单的类称为OrderService,可以从商店进行购买。在OrderService类中,必须自动关联商店的依赖关系。

在setter上使用@Autowired注释

setter方法上的@Autowired注释等效于使用配置文件进行自动装配中的autowiring =" byType"。

public interface OrderService {
  public void buyItems();
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowired on Setter
  @Autowired
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}
public interface IStore {
  public void doPurchase();
}
import org.springframework.stereotype.Service;

@Service
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Retail 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">
          
  <context:component-scan base-package="com.theitroad.springproject.service" />
</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(OrderServiceImpl.class);
    order.buyItems();
    // close the context
    context.close();
  }
}

在构造函数上使用@Autowired注释

使用配置文件自动装配时,bean的构造函数上的@Autowired注释等效于autowiring =" constructor"。

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowired on constructor
  @Autowired
  public OrderServiceImpl(IStore store){
      this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

如果目标bean只定义了一个构造函数,则不再需要Spring Framework 4.3以及此类构造函数上的@Autowired注释。但是,如果有几个可用的构造函数,则必须至少注释一个,以使容器知道要使用哪个构造函数。

在字段上使用@Autowired注释

使用配置文件进行自动装配时,@ Autowired字段上的注释等效于autowiring =" byType"。

@Service
public class OrderServiceImpl implements OrderService {
  // Autowiring on a field
  @Autowired
  private IStore store;	
  public void buyItems() {
    store.doPurchase();
  }
}

在任意方法上使用@Autowired注释

我们还可以将注释应用于具有任意名称和多个参数的方法。

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowiring on a method
  @Autowired
  public void prepare(IStore store) {
    this.store = store;

  }
  public void buyItems() {
    store.doPurchase();
  }
}

@Autowired的必需属性

默认情况下,当没有匹配的候选bean可用于给定的依赖项时,自动装配将失败。默认行为是将带注释的方法和字段视为必需的依赖项。

例如,在下面的bean类中,如果无法满足对存储的依赖关系,则会引发异常。

@Service
public class OrderServiceImpl implements OrderService {
  @Autowired
  private IStore store;
  ..
  ..
}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': 
Unsatisfied dependency expressed through field 'store'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'com.theitroad.springproject.service.IStore' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

要更改此默认行为,我们可以使用required = false以及@Autowired注释。如果无法自动连线,则忽略该属性。

@Autowired(required = false)
private IStore store;

从Spring Framework 5.0开始,我们还可以使用@Nullable注释来指示如果无法自动装配属性,则可以忽略该属性。

@Autowired
public void setStore(@Nullable IStore store) {
  this.store = store;
}

使用@Primary和基于注释的自动装配解决冲突

当按类型自动装配时,在这种情况下,Spring容器将无法确定要自动装配哪个bean并抛出NoUniqueBeanDefinitionException。

例如,如果有两个商店的IStore类型为RetailStore和OnlineStore。

@Service
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}

然后,我们的示例将失败,因为它无法确定要自动关联的商店。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': 
Unsatisfied dependency expressed through method 'setStore' parameter 0; 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: onlineStore,retailStore

使用Spring的@Primary注释,我们可以更好地控制选择过程。 @Primary表示当多个bean可以自动连接到单值依赖项的候选对象时,应优先使用特定的bean。

通过使用@Primary注释对OnlineStore bean进行注释,可以确保它具有优先级。

@Service
@Primary
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}

使用@Qualifier和基于注释的自动装配来解决冲突

Spring的@Qualifier注释可更好地控制选择过程。我们可以将限定符值与特定的参数相关联,从而缩小类型匹配的范围,以便为每个参数选择特定的bean。

@Service
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}
@Service
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Retail Store");
  }
}

有两个Istore类型相同的bean,通过使用@Qualifier注释,我们可以限定要自动装配的bean。

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  @Autowired
  @Qualifier("retailStore")
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

输出:

16:27:57.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'onlineStore'
16:27:57.981 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
16:27:58.108 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'retailStore'
Doing purchase from Retail Store