Skip to content

使用 @Autowired (Using @Autowired)

注意

在这一节的示例中,JSR 330 的 @Inject 注解可以替代 Spring 的 @Autowired 注解使用。详情请参阅这里

你可以将 @Autowired 注解应用于构造函数,如下例所示:

java
public class MovieRecommender {

	private final CustomerPreferenceDao customerPreferenceDao;

	@Autowired
	public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
		this.customerPreferenceDao = customerPreferenceDao;
	}

	// ...
}
kotlin
class MovieRecommender @Autowired constructor(
	private val customerPreferenceDao: CustomerPreferenceDao)

提示

如果目标 Bean 仅定义了一个构造函数,那么在该构造函数上使用 @Autowired 注解并不是必须的。但是,如果存在多个可用的构造函数且没有主要(Primary)或默认构造函数,则至少必须为一个构造函数添加 @Autowired 注解,以指示容器使用哪一个。有关详细信息,请参阅关于构造函数解析的讨论。

你可以将 @Autowired 注解应用于传统的 Setter 方法,如下例所示:

java
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	@Autowired
	public void setMovieFinder(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}

	// ...
}
kotlin
class SimpleMovieLister {

	@set:Autowired
	lateinit var movieFinder: MovieFinder

	// ...

}

你也可以将 @Autowired 应用于具有任意名称和多个参数的方法,如下例所示:

java
public class MovieRecommender {

	private MovieCatalog movieCatalog;

	private CustomerPreferenceDao customerPreferenceDao;

	@Autowired
	public void prepare(MovieCatalog movieCatalog,
			CustomerPreferenceDao customerPreferenceDao) {
		this.movieCatalog = movieCatalog;
		this.customerPreferenceDao = customerPreferenceDao;
	}

	// ...
}
kotlin
class MovieRecommender {

	private lateinit var movieCatalog: MovieCatalog

	private lateinit var customerPreferenceDao: CustomerPreferenceDao

	@Autowired
	fun prepare(movieCatalog: MovieCatalog,
				customerPreferenceDao: CustomerPreferenceDao) {
		this.movieCatalog = movieCatalog
		this.customerPreferenceDao = customerPreferenceDao
	}

	// ...
}

你也可以将 @Autowired 应用于字段,甚至将其与构造函数混合使用,如下例所示:

java
public class MovieRecommender {

	private final CustomerPreferenceDao customerPreferenceDao;

	@Autowired
	private MovieCatalog movieCatalog;

	@Autowired
	public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
		this.customerPreferenceDao = customerPreferenceDao;
	}

	// ...
}
kotlin
class MovieRecommender @Autowired constructor(
	private val customerPreferenceDao: CustomerPreferenceDao) {

	@Autowired
	private lateinit var movieCatalog: MovieCatalog

	// ...
}

提示

通过 BeanFactory 能够解析的依赖: 确保你的目标组件(例如 MovieCatalogCustomerPreferenceDao)声明的类型与你在 @Autowired 注解的注入点中使用的类型一致。否则,注入可能会因为运行时找不到匹配类型而失败。

对于 XML 定义的 Bean 或通过类路径扫描发现的组件类,容器通常预先知道具体类型。但是,对于 @Bean 工厂方法,你需要确保声明的返回类型具有足够的表现力。对于实现多个接口的组件,或者某些可能通过实现类型引用的组件,请在工厂方法上声明最具体的返回类型(至少要与引用你 Bean 的注入点所要求的类型一样具体)。

自我注入 (Self Injection)

@Autowired 还会考虑自我引用(即引用回当前正在注入的 Bean)进行注入。

但是请注意,自我注入是一种回退机制。对其他组件的常规依赖始终具有优先级。从这个意义上说,自我引用不参与常规的自动装配候选者选择,因此特别是永远不会成为 Primary。相反,它们总是以最低优先级结束。

在实际开发中,你应该仅将自我引用作为最后的手段 —— 例如,通过 Bean 的事务代理调用同一实例上的其他方法。作为替代方案,在这种情况下可以考虑将受影响的方法重构为单独的委托 Bean。

另一种选择是使用 @Resource,它可以根据其唯一名称获取指向当前 Bean 的代理。

注意

尝试注入同一 @Configuration 类中的 @Bean 方法的结果实际上也是一种自我引用场景。要么在实际需要的候选方法签名中延迟解析此类引用(而不是在配置类中使用自动装配字段),要么将受影响的 @Bean 方法声明为 static,从而将它们与包含它们的配置类实例及其生命周期解耦。否则,此类 Bean 仅在回退阶段被考虑,其他配置类上匹配的 Bean 将被选为主要候选者(如果可用)。

通过将 @Autowired 注解添加到期望该类型数组的字段或方法中,你还可以指示 Spring 从 ApplicationContext 中提供特定类型的所有 Bean,如下例所示:

java
public class MovieRecommender {

	@Autowired
	private MovieCatalog[] movieCatalogs;

	// ...
}
kotlin
class MovieRecommender {

	@Autowired
	private lateinit var movieCatalogs: Array<MovieCatalog>

	// ...
}

这同样适用于有泛型类型的集合,如下例所示:

java
public class MovieRecommender {

	private Set<MovieCatalog> movieCatalogs;

	@Autowired
	public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
		this.movieCatalogs = movieCatalogs;
	}

	// ...
}
kotlin
class MovieRecommender {

	@Autowired
	lateinit var movieCatalogs: Set<MovieCatalog>

	// ...
}

提示

如果你希望数组或列表中的项按特定顺序排序,你的目标 Bean 可以实现 org.springframework.core.Ordered 接口,或者使用 @Order 或标准的 @Priority 注解。否则,它们的顺序遵循容器中相应目标 Bean 定义的注册顺序。

你可以在目标类级别和 @Bean 方法上声明 @Order 注解,甚至可以针对单个 Bean 定义(在使用同一个 Bean 类的多个定义的情况下)。@Order 值可能会影响注入点的优先级,但请注意,它们不会影响单例启动顺序,单例启动顺序是由依赖关系和 @DependsOn 声明决定的独立关注点。

请注意,配置类上的 @Order 注解仅影响启动时整个配置类集的评估顺序。此类配置级别的 order 值根本不会影响其中包含的 @Bean 方法。对于 Bean 级别的排序,每个 @Bean 方法都需要有自己的 @Order 注解,该注解适用于特定 Bean 类型(由工厂方法返回)的多个匹配项。

请注意,标准的 jakarta.annotation.Priority 注解在 @Bean 级别不可用,因为它不能被声明在方法上。其语义可以通过 @Order 值与每个类型的单个 Bean 上的 @Primary 结合使用来建模。

只要键类型是 String,甚至可以自动装配具有泛型类型的 Map 实例。Map 的值是所有符合预期类型的 Bean,而键是相应的 Bean 名称,如下例所示:

java
public class MovieRecommender {

	private Map<String, MovieCatalog> movieCatalogs;

	@Autowired
	public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
		this.movieCatalogs = movieCatalogs;
	}

	// ...
}
kotlin
class MovieRecommender {

	@Autowired
	lateinit var movieCatalogs: Map<String, MovieCatalog>

	// ...
}

默认情况下,当给定的注入点没有匹配的候选 Bean 可用时,自动装配将失败。在声明数组、集合或 Map 的情况下,预期至少有一个匹配的元素。

默认行为是将带注解的方法和字段视为指示必须的依赖(Required dependencies)。你可以根据演示更改此行为,通过将其标记为非必需(即在 @Autowired 中将 required 属性设置为 false),使框架能够跳过一个不可满足的注入点:

java
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	@Autowired(required = false)
	public void setMovieFinder(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}

	// ...
}
kotlin
class SimpleMovieLister {

	@Autowired(required = false)
	var movieFinder: MovieFinder? = null

	// ...
}

注意

如果依赖项(对于多参数方法,则是其中之一)不可用,则根本不会调用非必需的方法。在这种情况下,非必需字段根本不会自动填充,保留其默认值。

换句话说,将 required 属性设置为 false 表示出于自动装配的目的,相应的属性是可选的,如果无法自动装配,该属性将被忽略。这允许为属性分配默认值,这些默认值可以通过依赖注入进行可选地覆盖。

注入的构造函数和工厂方法参数是一个特殊情况,因为由于 Spring 的构造函数解析算法可能会处理多个构造函数,@Autowired 中的 required 属性具有略微不同的含义。默认情况下,构造函数和工厂方法参数实际上是必须的,但在单构造函数方案中有一些特殊规则,例如如果没有匹配的 Bean 可用,则多元素注入点(数组、集合、映射)解析为空实例。这允许一种通用的实现模式,其中所有依赖项都可以在唯一的、不带 @Autowired 注解的公开多参数构造函数中声明。

注意

任何给定的 Bean 类中只能有一个构造函数可以使用 required 属性设置为 true@Autowired 进行声明,以指示作为 Spring Bean 使用时要自动装配的那个构造函数。因此,如果 required 属性保留其默认值 true,则只能有一个构造函数标有 @Autowired。如果多个构造函数声明了该注解,则它们都必须声明 required=false 才能被视为自动装配的候选者(类似于 XML 中的 autowire=constructor)。将选择依赖项数量最多的且能够被满足的构造函数。如果候选者都不能被满足,则将使用主要/默认构造函数(如果存在)。类似地,如果一个类声明了多个构造函数但都没有添加 @Autowired 注解,则将使用主要/默认构造函数(如果存在)。如果一个类一开始就只声明了一个构造函数,即使没有添加注解,也总是会使用它。请注意,带注解的构造函数不必是公开的。

或者,你可以通过 Java 的 java.util.Optional 来表达特定依赖项的非必需性质,如下例所示:

java
public class SimpleMovieLister {

	@Autowired
	public void setMovieFinder(Optional<MovieFinder> movieFinder) {
		...
	}
}

你也可以使用参数级别的 @Nullable 注解(任何包中的任何类型,例如 JSpecify 的 org.jspecify.annotations.Nullable),或者直接利用 Kotlin 内置的空安全支持:

java
public class SimpleMovieLister {

	@Autowired
	public void setMovieFinder(@Nullable MovieFinder movieFinder) {
		...
	}
}
kotlin
class SimpleMovieLister {

	@Autowired
	var movieFinder: MovieFinder? = null

	// ...
}

你还可以为一些著名的可解析依赖接口使用 @AutowiredBeanFactoryApplicationContextEnvironmentResourceLoaderApplicationEventPublisherMessageSource。这些接口及其扩展接口(如 ConfigurableApplicationContextResourcePatternResolver)会自动被解析,无需特殊设置。以下示例自动装配了一个 ApplicationContext 对象:

java
public class MovieRecommender {

	@Autowired
	private ApplicationContext context;

	public MovieRecommender() {
	}

	// ...
}
kotlin
class MovieRecommender {

	@Autowired
	lateinit var context: ApplicationContext

	// ...
}

注意

@Autowired@Inject@Value@Resource 注解由 Spring 的 BeanPostProcessor 实现处理。这意味着你不能在你自己的 BeanPostProcessorBeanFactoryPostProcessor 类型(如果有的话)中应用这些注解。

这些类型必须通过使用 XML 或 Spring 的 @Bean 方法来显式“连接”。


补充教学 —— 玩转 @Autowired 的艺术

1. 构造函数注入 vs 字段注入 虽然 @Autowired 写在字段上最省事,但 Spring 官方(以及大部分大厂规范)更推荐使用构造函数注入

  • 优点
    • 不可变性:可以用 final 修饰字段,确保 Bean 初始化后不会被篡改。
    • 非空检查:容器启动时就会报错,而不是等到运行时报 NPE
    • 摆脱强耦合:即使不用 Spring,你也可以用 new 轻松地手动创建这个类并传入依赖进行单元测试。

2. 为什么注入集合?—— 策略模式的最佳实践 当你定义了一个接口(比如 PaymentService),并且有多个实现类(AliPay, WechatPay)时,你可以直接注入一个 List<PaymentService>。 Spring 会自动把所有的实现全塞进去。你可以配合 @Order 来控制它们在 List 里的顺序,或者在逻辑里根据类型选择用哪一个。

3. Optional、@Nullable 与 required=false 它们三个都能处理“找不到依赖”的情况,但含义略有不同:

  • required = false:是 Spring 框架层面的指令,告诉 Spring 如果找不到就别管了。
  • Optional<T>:是代码层面的优雅,提示开发者“这个依赖可能没有,请做好判空处理”。
  • @Nullable:更像是一个标记,通常用于辅助 IDE 的静态检查。

4. 经典的“循环依赖”难题 如果你在两个单例 Bean 的构造函数里互相 @Autowired,Spring 启动会报 BeanCurrentlyInCreationException

  • 解决方案
    1. 改用 Setter 注入。
    2. 在其中一个注入点加一个 @Lazy 注解(延迟注入代理对象,打破闭环)。
    3. 重构代码(通常循环依赖意味着层级设计有问题)。

5. 容器自带的“隐藏福利” 记住那几个可以直接注入的“大人物”:ApplicationContext, Environment, BeanFactory。 有时候你不想通过 implements ApplicationContextAware 来拿容器对象,直接 @Autowired 它们就行,效果完全一样,还更优雅。

Based on Spring Framework.