Spring框架@Autowired注解详解:依赖注入的核心实现
Spring的@Autowired注解用于自动依赖注入。Spring框架是建立在依赖注入基础上的,我们通过Spring bean配置文件来注入类的依赖。
Spring的@Autowired注解
通常在Spring的bean配置文件中,我们提供了bean的配置细节,并且使用ref属性来指定将注入到其他bean中的beans。但是Spring框架也提供了自动装配的功能,这样我们就不需要明确提供bean的注入细节。有不同的方式可以实现自动装配Spring的bean:
- 按名称自动装配 – 对于这种类型的自动装配,我们使用setter方法进行依赖注入。同时,在需要注入依赖的类和Spring Bean配置文件中,变量名称应该相同。
- 按类型自动装配 – 对于这种类型的自动装配,我们使用类的类型。因此,在Spring Bean配置文件中只能配置一个此类型的Bean。
- 按构造器自动装配 – 这种方式与按类型自动装配几乎类似,唯一的区别是使用构造器进行依赖注入。
- 自动探测自动装配 – 如果你使用的是Spring 3.0或更早的版本,这是一个可用的自动装配选项之一。该选项可以根据Spring容器确定是按构造器自动装配还是按类型自动装配。由于我们已经有了很多选项,这个选项已被弃用。我在本教程中不会涵盖这个选项。
- @Autowired注解 – 我们可以使用Spring的@Autowired注解进行Bean自动装配。@Autowired注解可以应用于变量和方法,实现按类型自动装配。我们还可以在构造器上使用@Autowired注解进行基于构造器的自动装配。为了让@Autowired注解生效,我们还需要在Spring Bean配置文件中启用基于注解的配置。可以通过context:annotation-config元素或定义一个类型为org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor的Bean来实现。
- @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>
<!-- 通用属性 -->
<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>
<!-- 日志 -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring和事务 -->
<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>
<!-- 使用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.Olivia.spring.autowiring.model;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Autowired注解 – 服务类
让我们创建我们的服务类,在这个类中我们将通过Spring的自动装配来注入员工bean。
package com.Olivia.spring.autowiring.service;
import com.Olivia.spring.autowiring.model.Employee;
public class EmployeeService {
private Employee employee;
// 构造函数用于按构造函数自动装配
public EmployeeService(Employee emp) {
System.out.println("使用构造函数自动装配");
this.employee = emp;
}
// 默认构造函数,避免按名称或按类型自动装配时出现BeanInstantiationException
public EmployeeService() {
System.out.println("使用默认构造函数");
}
// 用于按名称和按类型自动装配
public void setEmployee(Employee emp) {
this.employee = emp;
}
public Employee getEmployee() {
return this.employee;
}
}
我们将使用相同的服务类来执行Spring的按名称、按类型和按构造函数进行自动装配。Setter方法将用于按名称和按类型进行Spring自动装配,而构造函数的注入将使用构造器的自动装配属性。当我们使用Spring的按名称或按类型进行自动装配时,会使用默认构造函数。这就是为什么我们明确地为EmployeeService的bean定义了默认构造函数。
Spring的@Autowired注解 – 按类型自动装配的示例
让我们为按类型自动装配创建一个使用Spring @Autowired注解的独立类。
这是文章《春季注解@Autowired》的第2部分(共4部分)。
package com.Olivia.spring.autowiring.service;
import org.springframework.beans.factory.annotation.Autowired;
import com.Olivia.spring.autowiring.model.Employee;
public class EmployeeAutowiredByTypeService {
//Autowired annotation on variable/setters is equivalent to 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的@Autowired注解和通过构造函数的@Qualifier Bean自动装配示例
让我们创建另一个服务类,我们将在其中使用@Autowired注解进行构造器注入。我们还将看到@Qualifier注解的使用。
package com.Olivia.spring.autowiring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.Olivia.spring.autowiring.model.Employee;
public class EmployeeAutowiredByConstructorService {
private Employee employee;
//Autowired annotation on Constructor is equivalent to 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.Olivia.spring.autowiring.model.Employee">
<property name="name" value="Pankaj"></property>
</bean>
<bean name="employee1" class="com.Olivia.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.Olivia.spring.autowiring.service.EmployeeService" autowire="byName" />
<!-- autowiring byType, there should be only one bean definition for the mapping -->
<bean name="employeeServiceByType" class="com.Olivia.spring.autowiring.service.EmployeeService" autowire="byType" />
<!-- autowiring by constructor -->
<bean name="employeeServiceConstructor" class="com.Olivia.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.Olivia.spring.autowiring.service.EmployeeAutowiredByTypeService" />
<bean name="employeeAutowiredByConstructorService" class="com.Olivia.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.Olivia.spring.autowiring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.Olivia.spring.autowiring.service.EmployeeAutowiredByConstructorService;
import com.Olivia.spring.autowiring.service.EmployeeAutowiredByTypeService;
import com.Olivia.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”属性,当执行以上的main方法时,我们将会得到下面的错误信息。
线程"main"中的异常 org.springframework.beans.factory.UnsatisfiedDependencyException: 创建在类路径资源[spring.xml]中定义的名为'employeeServiceByType'的bean时出错: 通过bean属性'employee'表示的不满足依赖: : 没有定义类型为[com.Olivia.spring.autowiring.model.Employee]的限定bean: 期望找到单个匹配bean但找到了2个: employee,employee1; 嵌套异常是 org.springframework.beans.factory.NoUniqueBeanDefinitionException: 没有定义类型为[com.Olivia.spring.autowiring.model.Employee]的限定bean: 期望找到单个匹配bean但找到了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.Olivia.spring.autowiring.main.SpringMain.main(SpringMain.java:12)
原因: org.springframework.beans.factory.NoUniqueBeanDefinitionException: 没有定义类型为[com.Olivia.spring.autowiring.model.Employee]的限定bean: 期望找到单个匹配bean但找到了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 Bean自动装配项目