1. 概述
本文我们将快速学习 org.springframework.beans.factory.annotation
和 org.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的名称与工厂方法相同。如果我们想要使用不同的名称, 可以通过这个注解的name或value参数来实现(参数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上找到。