Spring @Autowired 注釋

Spring @Autowired 注釋用於自動依賴注入。 Spring 框架 建立在 依賴注入 上,我們通過 Spring bean 配置文件注入類的依賴。

Spring @Autowired 注釋

通常我們在 Spring bean 配置文件中提供 bean 配置詳情,同時還使用 ref 屬性指定將被注入到其他 bean 中的 beans。但 Spring 框架還提供了自動連線功能,我們不需要明確提供 bean 注入詳情。有多種方式可以自動連線 Spring bean。

  1. 按名稱自動連線 – 對於這種類型的自動連線,setter 方法用於依賴注入。同時,在我們將依賴注入的類中和 Spring bean 配置文件中的變量名稱應該相同。
  2. autowire byType – 對於這種自動裝配,使用類型進行裝配。因此,在Spring bean配置文件中應該只配置一個針對此類型的bean。
  3. autowire by constructor – 這幾乎與autowire byType相似,唯一的區別是使用構造函數來注入依賴關係。
  4. autowire by autodetect – 如果您使用的是Spring 3.0或更舊的版本,這是可用的autowire選項之一。此選項用於由Spring容器確定的構造函數或byType的自動裝配。由於我們已經有很多選擇,此選項已被棄用。我將在本教程中不涉及此選項。
  5. @Autowired 註解 – 我們可以使用Spring @Autowired 註解進行spring bean自動裝配。@Autowired 註解可應用於按類型進行自動裝配的變量和方法。我們還可以在構造函數上使用 @Autowired 註解進行基於構造函數的spring自動裝配。為了使 @Autowired 註解工作,我們還需要在spring bean配置文件中啟用基於註解的配置。這可以通過 context:annotation-config 元素或定義一個 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 類型的bean 來完成。
  6. @Qualifier 注解 – 此注解用于避免在 bean 映射中发生冲突,我们需要提供将用于自动装配的 bean 名称。通过这种方式,我们可以避免为相同类型定义多个 bean 导致的问题。该注解通常与 @Autowired 注解一起使用。对于具有多个参数的构造函数,我们可以在方法中使用此注解并提供参数名称。

默认情况下,Spring Bean 的自动装配是关闭的。Spring Bean 的自动装配默认值为“default”,这意味着不执行自动装配。autowire 值为“no”也具有相同的行为。为了展示 Spring Bean 的自动装配的用法,让我们创建一个简单的 Spring Maven 项目。我们的最终项目将如下图所示。 让我们逐一查看每个自动装配选项。为此,我们将创建一个模型 bean 和一个服务类,在其中我们将注入模型 bean。

Spring @Autowired 注解 – Maven 依赖

对于 Spring 的自动装配,我们不需要添加任何额外的依赖项。我们的 pom.xml 文件包含 Spring 框架核心依赖项,如下所示。

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringBeanAutowiring</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

	</dependencies>
</project>

Spring @Autowired 註釋 – 模型 Bean

讓我們創建一個簡單的Java Bean,名為 Employee。這個 Bean 將具有一個單一的屬性,並帶有 getter 和 setter 方法。我們將在 Spring Bean 配置文件中初始化此屬性值。

package com.journaldev.spring.autowiring.model;

public class Employee {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Spring @Autowired 註釋 – 服務類

讓我們創建我們的服務類,我們將通過 Spring 自動裝配注入 Employee Bean。

package com.journaldev.spring.autowiring.service;

import com.journaldev.spring.autowiring.model.Employee;

public class EmployeeService {

	private Employee employee;

	// 構造函數用於按構造函數自動裝配
	public EmployeeService(Employee emp) {
		System.out.println("Autowiring by constructor used");
		this.employee = emp;
	}

	// 默認構造函數,以避免 BeanInstantiationException 自動裝配
	// 按名稱或按類型
	public EmployeeService() {
		System.out.println("Default Constructor used");
	}

	// 用於按名稱和按類型自動裝配
	public void setEmployee(Employee emp) {
		this.employee = emp;
	}

	public Employee getEmployee() {
		return this.employee;
	}
}

我們將使用相同的服務類來執行Spring按名字、按類型和按構造函數的自動裝配。設置器方法將用於Spring按名字和按類型的自動裝配,而構造函數注入將由構造函數的autowire屬性使用。當我們使用Spring按名字或按類型進行自動裝配時,將使用默認構造函數。這就是為什麼我們為EmployeeService bean明確定義了默認構造函數。

Spring @Autowired 註解 – 按類型自動裝配示例

讓我們為Spring注解創建一個獨立的類,以進行按類型的自動裝配。

package com.journaldev.spring.autowiring.service;

import org.springframework.beans.factory.annotation.Autowired;

import com.journaldev.spring.autowiring.model.Employee;

public class EmployeeAutowiredByTypeService {

	//在變量/設置器上的Autowired註解等同於autowire="byType"
	@Autowired
	private Employee employee;
	
	@Autowired
	public void setEmployee(Employee emp){
		this.employee=emp;
	}
	
	public Employee getEmployee(){
		return this.employee;
	}
}

請注意,我已經用Spring @Autowired註解註釋了Employee變量及其設置器方法,但這兩者中只需要一個就足以進行Spring bean的自動裝配。

Spring@Autowired註釋和@Qualifier通過構造函數自動裝配的示例

讓我們創建另一個服務類,在這裡我們將使用@Autowired註釋進行基於構造函數的注入。我們還將看到@Qualifier註釋的使用。

package com.journaldev.spring.autowiring.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.journaldev.spring.autowiring.model.Employee;

public class EmployeeAutowiredByConstructorService {

	private Employee employee;

	//構造函數上的Autowired註釋等同於autowire="constructor"
	@Autowired(required=false)
	public EmployeeAutowiredByConstructorService(@Qualifier("employee") Employee emp){
		this.employee=emp;
	}
	
	public Employee getEmployee() {
		return this.employee;
	}
}

當Spring框架初始化這個bean時,將使用名稱為”employee”的bean進行自動裝配。Spring @Autowired註釋接受一個參數”required”,默認值為TRUE的布爾值。我們可以將其定義為”false”,這樣如果沒有適合的bean用於自動裝配,Spring框架就不會拋出任何異常。

Spring @Autowired 註解 – Bean 配置文件

Spring bean 配置文件是任何 Spring 應用程序的主要部分,讓我們看看我們的 Spring bean 配置文件的外觀,然後我們將深入研究它的每個部分。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.0.xsd"
		
		default-autowire="byName" default-autowire-candidates="*" >

<bean name="employee" class="com.journaldev.spring.autowiring.model.Employee">
	<property name="name" value="Pankaj"></property>
</bean>

<bean name="employee1" class="com.journaldev.spring.autowiring.model.Employee" autowire-candidate="false">
	<property name="name" value="Dummy Name"></property>
</bean>

<!-- autowiring byName, bean name should be same as the property name -->
<bean name="employeeServiceByName" class="com.journaldev.spring.autowiring.service.EmployeeService" autowire="byName" />

<!-- autowiring byType, there should be only one bean definition for the mapping -->
<bean name="employeeServiceByType" class="com.journaldev.spring.autowiring.service.EmployeeService" autowire="byType" />

<!-- autowiring by constructor -->
<bean name="employeeServiceConstructor" class="com.journaldev.spring.autowiring.service.EmployeeService" autowire="constructor" />

<!-- Enable Annotation based configuration -->
<context:annotation-config />

<!-- using @Autowiring annotation in below beans, byType and constructor -->
<bean name="employeeAutowiredByTypeService" class="com.journaldev.spring.autowiring.service.EmployeeAutowiredByTypeService" />
<bean name="employeeAutowiredByConstructorService" class="com.journaldev.spring.autowiring.service.EmployeeAutowiredByConstructorService" />
</beans>

關於 Spring bean 配置文件的重要點包括:

  • beans 元素 default-autowire 用於定義默認的自動裝配方法。在這裡,我將默認的自動裝配方法定義為 byName。
  • beans 元素 default-autowire-candidates 用於提供可用於自動裝配的 bean 名稱的模式。為了簡單起見,我允許所有 bean 定義都符合自動裝配的條件,但是如果我們可以為自動裝配定義一些模式。例如,如果我們只想要將 DAO bean 定義用於自動裝配,我們可以將其指定為 default-autowire-candidates="*DAO"
  • autowire-candidate="false" 用於 bean 定義中,使其不符合自動裝配的條件。當我們對於單一類型有多個 bean 定義,並且我們希望其中一些不被自動裝配時,這很有用。例如,在上述 Spring bean 配置中,“employee1” bean 將不會被用於自動裝配。
  • autowire 屬性 byName、byType 和 constructor 是自明的,沒有太多需要解釋的。
  • context:annotation-config 用於啟用基於註釋的配置支援。請注意,employeeAutowiredByTypeService 和 employeeAutowiredByConstructorService 這兩個 bean 沒有 autowire 屬性。

Spring @Autowired註釋 – 測試程式

現在我們的 Spring 應用程式已經準備好了各種類型的 Spring 自動連線,讓我們撰寫一個簡單的測試程式來檢查它是否按預期運作。

package com.journaldev.spring.autowiring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.autowiring.service.EmployeeAutowiredByConstructorService;
import com.journaldev.spring.autowiring.service.EmployeeAutowiredByTypeService;
import com.journaldev.spring.autowiring.service.EmployeeService;

public class SpringMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		EmployeeService serviceByName = ctx.getBean("employeeServiceByName", EmployeeService.class);
		
		System.out.println("Autowiring byName. Employee Name="+serviceByName.getEmployee().getName());
		
		EmployeeService serviceByType = ctx.getBean("employeeServiceByType", EmployeeService.class);
		
		System.out.println("Autowiring byType. Employee Name="+serviceByType.getEmployee().getName());
		
		EmployeeService serviceByConstructor = ctx.getBean("employeeServiceConstructor", EmployeeService.class);
		
		System.out.println("Autowiring by Constructor. Employee Name="+serviceByConstructor.getEmployee().getName());
		
		//列印 hashcode 以確認所有物件都是不同類型的
		System.out.println(serviceByName.hashCode()+"::"+serviceByType.hashCode()+"::"+serviceByConstructor.hashCode());
		
		//測試 @Autowired 註釋
		EmployeeAutowiredByTypeService autowiredByTypeService = ctx.getBean("employeeAutowiredByTypeService",EmployeeAutowiredByTypeService.class);
		
		System.out.println("@Autowired byType. Employee Name="+autowiredByTypeService.getEmployee().getName());

		EmployeeAutowiredByConstructorService autowiredByConstructorService = ctx.getBean("employeeAutowiredByConstructorService",EmployeeAutowiredByConstructorService.class);
		
		System.out.println("@Autowired by Constructor. Employee Name="+autowiredByConstructorService.getEmployee().getName());

		ctx.close();
	}
}

這個程式很簡單,我們只是創建了 Spring 應用程式上下文並使用它來獲取不同的 bean,然後印出員工的名字。當我們運行上述應用程式時,我們會得到以下輸出。

Mar 31, 2014 10:41:58 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Mon Mar 31 22:41:58 PDT 2014]; root of context hierarchy
Mar 31, 2014 10:41:58 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Default Constructor used
Default Constructor used
Autowiring by constructor used
Autowiring byName. Employee Name=Pankaj
Autowiring byType. Employee Name=Pankaj
Autowiring by Constructor. Employee Name=Pankaj
21594592::15571401::1863015320
@Autowired byType. Employee Name=Pankaj
@Autowired by Constructor. Employee Name=Pankaj
Mar 31, 2014 10:41:58 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Mon Mar 31 22:41:58 PDT 2014]; root of context hierarchy

正如您所看到的,對於按名稱和按類型自動裝配,默認使用無參數構造函數來初始化bean。對於按構造函數自動裝配,則使用基於參數的構造函數。從所有變量的哈希碼中,我們確認所有的Spring beans都是不同的對象,而不是引用同一個對象。由於我們從自動裝配的合格bean列表中刪除了“employee1”,因此在bean映射中沒有混淆。如果從“employee1”的定義中刪除`autowire-candidate=”false”`,則在執行上述主方法時將獲得下面的錯誤消息。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeServiceByType' defined in class path resource [spring.xml]: Unsatisfied dependency expressed through bean property 'employee': : No qualifying bean of type [com.journaldev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.journaldev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1278)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1170)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at com.journaldev.spring.autowiring.main.SpringMain.main(SpringMain.java:12)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.journaldev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:967)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:855)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1263)
	... 13 more

這就是有關Spring @Autowired 註解和Spring自動裝配功能的所有內容,請從下面的鏈接下載示例項目並進行分析以了解更多。

下載Spring Bean自動裝配項目

Source:
https://www.digitalocean.com/community/tutorials/spring-autowired-annotation