Spring @Autowired 注解

Spring的@Autowired注解用于自动依赖注入。Spring框架建立在依赖注入的基础上,我们通过Spring的bean配置文件来注入类的依赖关系。

Spring @Autowired注解

通常,我们在Spring的bean配置文件中提供bean配置细节,并使用ref属性指定将注入其他bean的bean。但是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或更早版本,则这是其中一个可用的自动装配选项。此选项用于根据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项目。我们的最终项目将如下图所示。让我们逐个查看每个自动装配选项。为此,我们将创建一个Model bean和一个service类,在其中我们将注入Model 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的按名称、按类型和按构造函数自动装配。Setter方法将用于Spring的按名称和按类型自动装配,而构造函数基于注入将使用构造函数自动装配属性。当我们使用Spring的按名称或按类型自动装配时,默认构造函数会被使用。这就是为什么我们为EmployeeService bean显式定义了默认构造函数。

Spring@Autowired注解-按类型自动装配示例

让我们创建一个单独的类,其中包含Spring@Autowired注解,用于按类型自动装配。

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变量及其setter方法进行了注解,但是只有其中之一对于spring bean自动装配是足够的。

Spring注解和@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;
	}
}

当此bean由Spring框架初始化时,名称为“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` beans 没有 `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());
		
		// 打印哈希码以确认所有对象的类型都不同
		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 Bean 都是不同的对象,而不是引用相同的对象。由于我们从自动装配的合格 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