Spring @Autowired 注解
Spring @Autowired注释用于自动依赖注入。Spring框架建立在依赖注入的基础上,我们通过 spring bean 配置文件注入类依赖项。
Spring @Autowired注解
通常我们在 Spring Bean 配置文件中提供 Bean 配置详细信息,并且我们还使用属性指定将注入其他 Bean 的 Bean ref
。但是 Spring 框架也提供了自动装配功能,我们不需要明确提供 Bean 注入详细信息。我们可以通过不同的方式自动装配 Spring Bean。
- autowire byName - 对于这种类型的自动装配,setter 方法用于依赖注入。此外,我们将注入依赖项的类中的变量名称和 spring bean 配置文件中的变量名称应该相同。
- autowire byType - 对于这种类型的自动装配,使用类类型。因此,在 spring bean 配置文件中应该只为这种类型配置一个 bean。
- 通过构造函数自动装配- 这与通过类型自动装配几乎类似,唯一的区别是使用构造函数来注入依赖项。
- 通过自动检测自动装配 - 如果您使用的是 Spring 3.0 或更早版本,这是可用的自动装配选项之一。此选项用于通过构造函数或按类型自动装配,由 Spring 容器决定。由于我们已经有这么多选项,因此不推荐使用此选项。在本教程中我不会介绍此选项。
@Autowired
注释 - 我们可以使用 Spring @Autowired注释进行 Spring bean 自动装配。@Autowired注释可以应用于变量和方法,以便按类型自动装配。我们还可以在构造函数上使用@Autowired注释,以实现基于构造函数的 Spring 自动装配。为了使@Autowired注释正常工作,我们还需要在 Spring bean 配置文件中启用基于注释的配置。这可以通过context:annotation-config元素或定义类型的 bean 来完成org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
。@Qualifier
注释 - 此注释用于避免 bean 映射中的冲突,我们需要提供将用于自动装配的 bean 名称。这样,我们可以避免为同一类型定义多个 bean 的问题。此注释通常与@Autowired注释一起使用。对于具有多个参数的构造函数,我们可以将此注释与方法中的参数名称一起使用。
By default spring bean autowiring is turned off. Spring bean autowire default value is “default” that means no autowiring is to be performed. autowire value “no” also have the same behavior. To showcase the use of Spring Bean autowiring, let’s create a simple Spring Maven project. Our final project will look like below image. Let’s look into each of the autowire options one by one. For that we will create a Model bean and a service class where we will inject the model bean.
Spring @Autowired Annotation - Maven Dependencies
For spring autowiring, we don’t need to add any additional dependencies. Our pom.xml file has spring framework core dependencies and looks like below.
<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 Annotation - Model Bean
Let’s create a simple Java Bean, named Employee. This bean will have a single property with getter and setter methods. We will initialize this property value in the spring bean configuration file.
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 Annotation - Service Class
Let’s create our service class in which we will inject Employee bean through spring autowiring.
package com.journaldev.spring.autowiring.service;
import com.journaldev.spring.autowiring.model.Employee;
public class EmployeeService {
private Employee employee;
// constructor is used for autowire by constructor
public EmployeeService(Employee emp) {
System.out.println("Autowiring by constructor used");
this.employee = emp;
}
// default constructor to avoid BeanInstantiationException for autowire
// byName or byType
public EmployeeService() {
System.out.println("Default Constructor used");
}
// used for autowire byName and byType
public void setEmployee(Employee emp) {
this.employee = emp;
}
public Employee getEmployee() {
return this.employee;
}
}
We will use the same service class for perform spring autowiring byName, byType and by constructor. The setter method will be used for spring autowiring byName and byType whereas constructor based injection will be used by constructor autowire attribute. When we use spring autowire byName or byType, default constructor is used. That’s why we have explicitly defined the default constructor for the EmployeeService bean.
Spring @Autowired Annotation - autowiring byType Example
Let’s create a separate class with Spring @Autowired annotation for autowiring byType.
package com.journaldev.spring.autowiring.service;
import org.springframework.beans.factory.annotation.Autowired;
import com.journaldev.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;
}
}
Note that I have annotated both Employee variable and it’s setter method with Spring @Autowired annotation, however only one of these is sufficient for spring bean autowiring.
Spring @Autowired Annotation and @Qualifier Bean autowiring by constructor Example
Let’s create another service class where we will use @Autowired annotation for constructor based injection. We will also see @Qualifier annotation usage.
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 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;
}
}
When this bean will be initialized by Spring framework, bean with name as “employee” will be used for autowiring. Spring @Autowired annotation excepts one argument “required” that is a boolean with default value as TRUE. We can define it to be “false” so that spring framework don’t throw any exception if no suitable bean is found for autowiring.
Spring @Autowired Annotation - Bean Configuration File
Spring bean configuration file is the main part of any spring application, let’s see how our spring bean configuration file looks and then we will look into each part of it.
<?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 将不用于自动装配。- 自动装配属性 byName、byType 和构造函数是不言而喻的,没有什么可解释的。
context:annotation-config
用于启用基于注释的配置支持。请注意,employeeAutowiredByTypeService 和 employeeAutowiredByConstructorService bean 没有自动装配属性。
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());
//printing hashcode to confirm all the objects are of different type
System.out.println(serviceByName.hashCode()+"::"+serviceByType.hashCode()+"::"+serviceByConstructor.hashCode());
//Testing @Autowired annotations
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 映射中没有混淆。如果我们autowire-candidate="false"
从“employee1”定义中删除,则在执行上述主要方法时将收到以下错误消息。
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 自动装配项目