1. 概述

本文我们将快速学习 org.springframework.beans.factory.annotationorg.springframework.context.annotation 包下的注解实现依赖注入。 我们通常称这些注解为 "Spring核心注解"。

2. 依赖注入相关注解

2.1. @Autowired

使用 @Autowired 注解,用于标记告诉Spring需要解析和注入的依赖。提供多种注入方式

构造器注入方式:

class Car {
    Engine engine;

    @Autowired
    Car(Engine engine) {
        this.engine = engine;
    }
}

Setter 注入方式:

class Car {
    Engine engine;

    @Autowired
    void setEngine(Engine engine) {
        this.engine = engine;
    }
}

字段注入:

class Car {
    @Autowired
    Engine engine;
}

@Autowired 有一个 required 参数,默认值为 true。为 true 时,如果找不到满足的依赖会抛出异常,否则不会进行装配。

注意,如果我们使用构造器方式注入,所有构造器参数都是强制的。

从4.3版本开始,除非我们声明了至少两个构造器,否则不需要显式地用 @Autowired 注解构造器。

更多详细信息请访问我们关于 @Autowired构造器注入 的文章。

2.2. @Bean

@Bean 注解标记一个工厂方法,用来实例化 Spring bean:

@Bean
Engine engine() {
    return new Engine();
}

Spring使用该工厂生成Bean对象,只调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

生成的bean的名称与工厂方法相同。如果我们想要使用不同的名称, 可以通过这个注解的namevalue参数来实现(参数value是参数name的别名):

@Bean("engine")
Engine getEngine() {
    return new Engine();
}

注意, 所有使用 @Bean 注解的方法都必须位于 @Configuration 类中。

2.3. @Qualifier - 歧义消除

@Qualifier 注解配合 @Autowired 一起使用,用于解决 依赖注入时的歧义性问题

例如,以下两个 bean 实现了相同的接口:

class Bike implements Vehicle {}

class Car implements Vehicle {}

例如现在我们需要注入一个Vehicle bean,Spring不知道到底是需要Bike还是Car实现呢?在这种情况下,我们可以使用*@Qualifier*注解显式地提供一个bean的名称。

构造器注入中使用:

@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

setter注入:

@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

或:

@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
    this.vehicle = vehicle;
}

使用字段注入:

@Autowired
@Qualifier("bike")
Vehicle vehicle;

有关更详细的描述,请阅读这篇文章

2.4. @Value - 属性注入

@Value 注解用于将property属性值注入到bean中。

构造器方式:

Engine(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Setter 注入:

@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Setter 注入:

@Value("8")
void setCylinderCount(int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

字段注入:

@Value("8")
int cylinderCount;

上面演示的是静态字面量值注入,没啥实用价值。我们可以在 @Value 中使用 占位符字符串 来注入 在外部源中定义的值,例如在 .properties.yaml 文件中定义的值。

假设我们有以下 .properties 文件:

engine.fuelType=petrol

我们可以使用以下方式注入 engine.fuelType 的值:

@Value("${engine.fuelType}")
String fuelType;

我们甚至可以在 @Value 中使用 SpEL(Spring 表达式语言)。更多高级示例可以在我们的 @Value 注解文章中找到。

2.5. @DependsOn - 依赖顺序

我们可以使用@DependsOn注解来让 Spring 在初始化被注解的 bean 之前先初始化其他 bean。通常,这种行为是自动的,基于 bean 之间的显式依赖关系。

我们只有在依赖关系是隐式的时候才需要这个注解,例如,JDBC 驱动程序加载或静态变量初始化。

我们可以在依赖类上使用 @DependsOn,指定依赖 bean 的名称。注解的 value 参数需要一个包含依赖 bean 名称的数组:

@DependsOn("engine")
class Car implements Vehicle {}

另外,如果我们使用 @Bean 注解定义一个 bean,工厂方法应该用 @DependsOn 注解:

@Bean
@DependsOn("fuel")
Engine engine() {
    return new Engine();
}

2.6. @Lazy - 懒加载

使用 @Lazy 注解可实现懒加载,延迟初始化我们的 bean。默认情况下,Spring 在应用程序上下文启动/引导时就会创建所有单例 bean。

然而,有些情况下我们需要在请求时创建 bean,而不是在应用程序启动时

这个注解的行为会根据我们放置的确切位置而有所不同。我们可以将它放在:

  • 一个带有 @Bean 注解的 bean 工厂方法上,以延迟方法调用(从而延迟 bean 的创建)
  • 一个 @Configuration 类上,这将影响所有包含的 @Bean 方法
  • 一个不是 @Configuration 类的 @Component 类上,这个 bean 将被懒初始化
  • 一个 @Autowired 构造函数、setter 或字段上,以懒加载依赖项本身(通过代理)

@Lazy 注解的 value 参数默认值为 true。它可用于覆盖默认行为。

例如,当全局设置为懒加载时,将 bean 标记为立即加载,或者在标有 @Lazy@Configuration 类中将特定的 @Bean 方法配置为立即加载:

@Configuration
@Lazy
class VehicleFactoryConfig {

    @Bean
    @Lazy(false)
    Engine engine() {
        return new Engine();
    }
}

要了解更多信息,请访问这篇文章

2.7. @Lookup

使用 @Lookup 注解的方法告诉 Spring 在我们调用该方法时返回方法返回类型的实例。

关于这个注解的详细信息可以在这篇文章中找到

2.8. @Primary

有时我们需要定义多个相同类型的 bean。在这些情况下,注入将会失败,因为 Spring 不知道我们需要哪个 bean。

前面我们已经介绍如何使用 @Qualifier 注解来解决这一问题。

然而,大多数时候我们需要一个特定的 bean,而很少需要其他的。@Primary 注解可以用来标记优先加载的bean,即使容器中有多个同类型的bean,Spring也会优先加载带有@Primary注解的bean

@Component
@Primary
class Car implements Vehicle {}

@Component
class Bike implements Vehicle {}

@Component
class Driver {
    @Autowired
    Vehicle vehicle;
}

@Component
class Biker {
    @Autowired
    @Qualifier("bike")
    Vehicle vehicle;
}

在前面的例子中,Car 是主要的交通工具。因此,在 Driver 类中,Spring 注入了一个 Car bean。当然,在 Biker bean 中,vehicle 字段的值将是一个 Bike 对象,因为它被限定了。

2.9. @Scope - 作用域

我们使用 @Scope 来定义 @Component 类或 @Bean 定义的作用域。它可以是 singleton、prototype、request、session、globalSession 或一些自定义作用域。

例如:

@Component
@Scope("prototype")
class Engine {}

3. 配置有关注解

3.1. @Profile

@Profile 实现多环境配置。如果希望我们的Spring Bean 只在特定配置文件被激活时才启用,我们可以使用 @Profile 注解来标记它:

@Component
@Profile("sportDay")
class Bike implements Vehicle {}

你可以在这篇文章中阅读更多关于profile的内容。

3.2. @Import

使用 @Import 注解可快速加载指定的 @Configuration 配置类,而无需进行组件扫描

@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

3.3. @ImportResource

使用@ImportResource注解 导入XML配置:

@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}

3.4. @PropertySource

使用*@PropertySource注解,我们可以加载属性配置文件:

@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}

由于 @PropertySource 利用了Java 8的重复注解特性,所以我们可以在一个类上多次标记该注解:

@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}

3.5. @PropertySources

用法同上,指定多个属性配置:

@Configuration
@PropertySources({ 
    @PropertySource("classpath:/annotations.properties"),
    @PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

4. 总结

在本文中,我们概述了最常用的Spring核心注解。我们了解了如何配置Bean的装配和应用程序上下文,以及如何标记类以进行组件扫描。

惯例,示例代码可以在GitHub上找到