12 Java-based Container Configuration
- 12.3使用@Bean注解
- 定义Bean
- Bean依赖关系
- 接收生命周期回调
- 确定Bean的作用域
- 定制化Bean命名
- Bean别名
- Bean 描述
本节的内容:如何使用注解配置 Spring 容器。包含下面的主题:
- 基本概念:@Bean 和 @Configuration
- 使用AnnotationConfigApplication初始化Spring容器
- 使用@Bean注解
- 使用@Configuration
- 编写基于java的配置
- Bean定义概要
- PropertySource抽象
- 使用@PropertySource
- 语句中占位符的解析
12.3使用@Bean注解
@Bean是方法级别的注解,与XML bean元素作用相同。该注解支持一些XML的bean元素中的一些属性,比如: * init-method * destroy-method * autowiring * 等属性。
定义Bean
要声明一个bean,可以使用@Bean注释来注解一个方法。使用@Bean注解的方法注册一个Bean定义作为该方法的返回值,注册的Bean在ApplicationContext的类型规范内。默认情况下,bean名与方法名相同。下面的例子显示了一个@Bean方法声明:
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
前面的配置与下面的Spring XML完全相同
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
这两个声明都使一个名为transferService的bean在ApplicationContext中可用,绑定到类型为TransferServiceImpl的对象实例,如下面的文本图像所示:
transferService -> com.acme.TransferServiceImpl
您还可以使用接口(或基类)返回类型声明@Bean方法,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
但是,这将高级类型预测的可见性限制为指定的接口类型(TransferService)。然后,容器只知道一次完整类型(TransferServiceImpl),受影响的单例bean就被实例化了。非延迟性单例bean根据它们的声明顺序被实例化,因此您可能会看到不同的类型匹配结果,这取决于另一个组件何时试图通过未声明的类型进行匹配(例如@Autowired TransferServiceImpl,它只在transferService bean实例化之后才会解析)。
注意
如果始终通过声明的服务接口引用类型,则@Bean返回类型可以安全地与设计的意图相吻合。但是,对于实现多个接口的组件或可能由其实现类型引用的组件,声明最特定的返回类型会更安全。(至少与引用bean的注入点所要求的一样具体)
Bean依赖关系
@bean注解的方法可以有任意数量的参数来描述构建该bean所需的依赖关系。
例如,如果我们的TransferService需要一个AccountRepository,我们可以用一个方法参数来实现这个依赖,如下面的例子所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
解析机制与基于构造的依赖注入非常相似。有关详细信息,请参阅Constructor-based Dependency Injection。
接收生命周期回调
使用@Bean注解定义的任何类都支持常规的生命周期回调,并且可以使用JSR-250中的@PostConstruct和@PreDestroy注解。也完全支持常规的Spring生命周期回调。如果一个bean实现了InitializingBean、DisposableBean或Lifecycle,容器将调用它们各自的方法。标准的*Aware接口集(如BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware等)也得到了完全支持。
@Bean注释支持指定任意初始化和销毁回调方法,很像Spring XML在bean元素上的init-method和destroy-method属性,如下面的示例所示:
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
注意
默认情况下,Java配置定义的bean拥有public 修饰的close或shtudown方法将被自动加入到销毁回调中。如果有public
修饰的close或shtudown方法,并且您不希望在容器关闭时调用它,那么您可以向bean定义中添加@Bean(destroyMethod="")来禁用默认(推断)模式。
在默认情况下,您可能希望对使用JNDI获得的资源这样做,因为它的生命周期是在应用程序之外管理的。特别要注意的是,一定要始终对Datasource这样做,因为在JavaEE应用服务器上这样做是有问题的。 下面的示例演示如何防止Datasource的自动销毁回调@Bean(destroyMethod="") public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS"); }
同样,对于@Bean方法,您通常使用可编程的JNDI查找,可以使用Spring的JndiTemplate或JndiLocatorDelegate助手,或者直接使用JNDI InitialContext,但不使用JndiObjectFactoryBean变体(这将迫使您将返回类型声明为FactoryBean类型,而不是实际的目标类型,这使得在其他想要引用这里提供的资源的@Bean方法中进行交叉引用调用时更加困难)。
对于上例中的BeanOne,在构造期间直接调用init()方法同样有效,如下例所示
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
当您直接在Java中工作时,您可以对对象做任何您喜欢的事情,而不总是需要依赖容器生命周期。
确定Bean的作用域
Spring包含@Scope注解,因此您可以指定bean的范围。
您可以指定使用@Bean注解定义的bean应该具有特定的范围。您可以使用Bean Scope部分中指定的任何标准作用域。
Scope | Description |
singleton | (默认)为每个Spring IoC容器将单个bean定义作用于单个对象实例。 |
prototype | 将单个bean定义作用于任意数量的对象实例。 |
request | 将单个bean定义的范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,这些实例是在单个bean定义的基础上创建的。仅在可感知web的Spring应用程序上下文中有效。 |
session | 将单个bean定义的范围限定为HTTP会话的生命周期。仅在可感知web的Spring应用程序上下文中有效。 |
application | 将单个bean定义作用于ServletContext的生命周期。仅在可感知web的Spring应用程序上下文中有效。 |
websocket | 将单个bean定义作用于WebSocket的生命周期。仅在可感知web的Spring应用程序上下文中有效。 |
从Spring 3.0开始,线程作用域可用,但默认情况下不注册。有关更多信息,请参阅SimpleThreadScope的文档。有关如何注册此或任何其他自定义范围的说明,请参阅Using a Custom Scope一章。
默认的范围是单例的,但是你可以用@Scope注解来覆盖它,如下面的例子所示:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
Spring通过作用域代理提供了处理作用域依赖项的方便方法。使用XML配置时创建这样一个代理的最简单方法是aop:scoped-proxy元素。用@Scope注释在Java中配置bean可以提供与proxyMode属性相同的支持。默认值是no proxy (ScopedProxyMode. no),但是您可以指定ScopedProxyMode。TARGET_CLASS或ScopedProxyMode.INTERFACES。
如果您使用Java将XML参考文档中的作用域代理示例(参见作用域代理)移植到我们的@Bean,它类似于以下内容:
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences());
return service;
}
定制化Bean命名
默认情况下,配置类使用@Bean方法的名称作为结果bean的名称。但是,可以使用name属性覆盖此功能,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean(name = "myThing")
public Thing thing() {
return new Thing();
}
}
Bean别名
正如在bean的命名中所讨论的,有时希望为单个bean赋予多个名称,称为bean别名。@Bean注释的name属性为此接受一个字符串数组。下面的例子展示了如何为一个bean设置多个别名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
Bean 描述
有时,提供bean的更详细的文本描述是有帮助的。当出于监控目的而公开bean(可能通过JMX)时,这一点特别有用
要向@Bean添加描述,可以使用@Description注释,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}