使用@Bean注解

@Bean是一个方法级别的注解,直接对应XML中的<bean/>元素。该注解支持<bean/>提供的一些属性,例如:

您可以在@Configuration注解或@Component注解的类中使用@Bean注解。

声明一个Bean

@Bean注解对方法进行注解。您可以使用此方法在 ApplicationContext中注册与方法返回值指定类型的bean定义。默认情况下,bean名称与方法名称相同。以下示例显示了一个 @Bean方法声明:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun transferService() = TransferServiceImpl()
}
<beans>
	<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
transferService的bean在 ApplicationContext中可用,绑定到类型为 TransferServiceImpl的对象实例,如下文所示:

transferService -> com.acme.TransferServiceImpl

  • Java

public interface BaseConfig {

	@Bean
	default TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}

@Configuration
public class AppConfig implements BaseConfig {

}
@Bean方法声明为接口(或基类)返回类型,如下例所示:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public TransferService transferService() {
		return new TransferServiceImpl();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun transferService(): TransferService {
		return TransferServiceImpl()
	}
}
TransferService)的高级类型预测的可见性。然后,一旦受影响的单例bean被实例化,容器才会知道完整类型( TransferServiceImpl)。非延迟加载的单例bean根据其声明顺序进行实例化,因此根据另一个组件何时尝试通过非声明类型(例如 @Autowired TransferServiceImpl)进行匹配,您可能会看到不同的类型匹配结果(仅在 transferService bean被实例化后才解析)。

如果您始终通过声明的服务接口引用类型,您的@Bean返回类型可以安全地遵循该设计决策。但是,对于实现多个接口的组件或可能按其实现类型引用的组件,最好声明尽可能具体的返回类型(至少与引用您的bean的注入点要求的一样具体)。

Bean依赖关系

@Bean注解的方法可以有任意数量的参数,描述构建该bean所需的依赖关系。例如,如果我们的 TransferService需要一个 AccountRepository,我们可以通过方法参数实现该依赖关系,如下例所示:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public TransferService transferService(AccountRepository accountRepository) {
		return new TransferServiceImpl(accountRepository);
	}
}
@Configuration
class AppConfig {

	@Bean
	fun transferService(accountRepository: AccountRepository): TransferService {
		return TransferServiceImpl(accountRepository)
	}
}

接收生命周期回调

任何使用@Bean注解定义的类都支持常规的生命周期回调,并可以使用JSR-250中的@PostConstruct@PreDestroy注解。有关详细信息,请参见JSR-250注解

常规的Spring生命周期回调也得到充分支持。如果一个bean实现了InitializingBeanDisposableBeanLifecycle,则容器会调用它们各自的方法。

标准的*Aware接口集(如BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware等)也得到充分支持。

@Bean注解支持指定任意的初始化和销毁回调方法,类似于Spring XML中bean元素上的init-methoddestroy-method属性,如下例所示:

  • Java

  • Kotlin

public class BeanOne {

	public void init() {
		// 初始化逻辑
	}
}

public class BeanTwo {

	public void cleanup() {
		// 销毁逻辑
	}
}

@Configuration
public class AppConfig {

	@Bean(initMethod = "init")
	public BeanOne beanOne() {
		return new BeanOne();
	}

	@Bean(destroyMethod = "cleanup")
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}
class BeanOne {

	fun init() {
		// 初始化逻辑
	}
}

class BeanTwo {

	fun cleanup() {
		// 销毁逻辑
	}
}

@Configuration
class AppConfig {

	@Bean(initMethod = "init")
	fun beanOne() = BeanOne()

	@Bean(destroyMethod = "cleanup")
	fun beanTwo() = BeanTwo()
}

默认情况下,使用Java配置定义的bean如果具有公共的closeshutdown方法,将自动注册到销毁回调中。如果您有一个公共的closeshutdown方法,但不希望在容器关闭时调用它,可以在bean定义中添加@Bean(destroyMethod = "")以禁用默认的(inferred)模式。

您可能希望对通过JNDI获取的资源默认执行此操作,因为其生命周期在应用程序之外进行管理。特别是,确保始终对DataSource执行此操作,因为在Jakarta EE应用服务器上已知存在问题。

以下示例显示了如何防止DataSource的自动销毁回调:

  • Java

  • Kotlin

@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
	return (DataSource) jndiTemplate.lookup("MyDS");
}
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
	return jndiTemplate.lookup("MyDS") as DataSource
}

此外,在@Bean方法中,通常使用编程方式的JNDI查找,可以使用Spring的JndiTemplateJndiLocatorDelegate助手,或直接使用JNDI的InitialContext,但不使用JndiObjectFactoryBean变体(这将强制您将返回类型声明为FactoryBean类型而不是实际目标类型,使其在其他@Bean方法中进行交叉引用调用时更难使用)。

在上面示例中的BeanOne的情况下,也可以在构造过程中直接调用init()方法,如下例所示:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public BeanOne beanOne() {
		BeanOne beanOne = new BeanOne();
		beanOne.init();
		return beanOne;
	}

	// ...
}
@Configuration
class AppConfig {

	@Bean
	fun beanOne() = BeanOne().apply {
		init()
	}

	// ...
}
当直接在Java中工作时,您可以随意处理对象,不一定总是依赖容器的生命周期。

指定Bean作用域

Spring包含@Scope注解,以便您可以指定bean的作用域。

使用@Scope注解

您可以指定使用@Bean注解定义的bean应具有特定的作用域。您可以使用Bean作用域部分中指定的任何标准作用域。

默认作用域是singleton,但您可以使用@Scope注解覆盖此设置,如下例所示:

  • Java

  • Kotlin

@Configuration
public class MyConfiguration {

	@Bean
	@Scope("prototype")
	public Encryptor encryptor() {
		// ...
	}
}
@Configuration
class MyConfiguration {

	@Bean
	@Scope("prototype")
	fun encryptor(): Encryptor {
		// ...
	}
}

@Scopescoped-proxy

Spring通过作用域代理为使用作用域的依赖项提供了一种方便的工作方式。在使用XML配置时创建这样的代理的最简单方法是使用<aop:scoped-proxy/>元素。在Java中使用@Scope注解配置bean提供了与proxyMode属性等效的支持。默认值为ScopedProxyMode.DEFAULT,通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。您可以指定ScopedProxyMode.TARGET_CLASSScopedProxyMode.INTERFACESScopedProxyMode.NO

如果您将XML参考文档中的作用域代理示例(请参阅作用域代理)转换为使用Java的@Bean,则如下所示:

  • Java

  • Kotlin

// 作为代理公开的HTTP会话作用域bean
@Bean
@SessionScope
public UserPreferences userPreferences() {
	return new UserPreferences();
}

@Bean
public Service userService() {
	UserService service = new SimpleUserService();
	// 对代理的userPreferences bean的引用
	service.setUserPreferences(userPreferences());
	return service;
}
// 作为代理公开的HTTP会话作用域bean
@Bean
@SessionScope
fun userPreferences() = UserPreferences()

@Bean
fun userService(): Service {
	return SimpleUserService().apply {
		// 对代理的userPreferences bean的引用
		setUserPreferences(userPreferences())
	}
}

自定义Bean命名

默认情况下,配置类使用@Bean方法的名称作为生成的bean的名称。但是,可以使用name属性覆盖此功能,如下例所示:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean("myThing")
	public Thing thing() {
		return new Thing();
	}
}
@Configuration
class AppConfig {

	@Bean("myThing")
	fun thing() = Thing()
}

Bean别名

如在命名Beans中所讨论的,有时希望给单个bean提供多个名称,也就是所谓的bean别名。@Bean注解的name属性接受一个String数组以实现此目的。以下示例显示了如何为bean设置多个别名:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
	public DataSource dataSource() {
		// 实例化、配置和返回DataSource bean...
	}
}
@Configuration
class AppConfig {

	@Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource")
	fun dataSource(): DataSource {
		// 实例化、配置和返回DataSource bean...
	}
}

Bean描述

有时,为bean提供更详细的文本描述是有帮助的。当bean被暴露(可能通过JMX)用于监控目的时,这可能特别有用。

要为@Bean添加描述,您可以使用@Description注解,如下例所示:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	@Description("提供一个bean的基本示例")
	public Thing thing() {
		return new Thing();
	}
}
@Configuration
class AppConfig {

	@Bean
	@Description("提供一个bean的基本示例")
	fun thing() = Thing()
}