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();
    }
}