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框架核心依赖,如下所示。

xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.samples

SpringBeanAutowiring

0.0.1-SNAPSHOT

1.6

UTF-8

UTF-8

4.0.2.RELEASE

1.0.13

1.7.5

org.springframework

spring-context

${spring-framework.version}

org.springframework

spring-tx

${spring-framework.version}

org.slf4j

slf4j-api

${slf4j.version}

compile

ch.qos.logback

logback-classic

${logback.version}

runtime

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 配置文件的外观,然后再逐个部分进行讨论。

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="*" >

有关于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.(ClassPathXmlApplicationContext.java:139)

at org.springframework.context.support.ClassPathXmlApplicationContext.(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自动装配项目

汽车之家
网站空间如何购买(网站空间购买攻略)