Spring Boot 教程

@Bean 注解

@Bean 注解指示方法生成由 Spring 容器管理的 Bean。

概述

该注释的属性的名称和语义有意类似于Spring XML模式中的 <bean/> 元素的名称和语义。例如:

@Bean
public MyBean myBean() {
	// instantiate and configure MyBean obj
    return obj;
}

Bean名称

当 name() 属性可用时,确定bean名称的默认策略是使用@Bean方法的名称。这是方便且直观的,但是如果需要显式命名,则可以使用name属性(或其别名值)。还要注意,name接受一个字符串数组,允许为单个bean使用多个名称(即主bean名称加上一个或多个别名)。

@Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
public MyBean myBean() {
    // instantiate and configure MyBean obj
    return obj;
}

Profile, Scope, Lazy, DependsOn, Primary, Order

请注意,@Bean 注解不提供 profile、scope、lazy、depends-on 或 primary 属性。相反,它应与 @Scope、@Lazy、@DependsOn 和 @Primary 注解结合使用以声明这些语义。例如:

@Bean
@Profile("production")
@Scope("prototype")
public MyBean myBean() {
    // instantiate and configure MyBean obj
    return obj;
}

上述注解的语义在组件类级别与它们的用法匹配:

  • @Profile 允许选择性地包含某些 bean

  • @Scope 将 bean 的范围从单例更改为指定的范围

  • @Lazy 仅在默认单例作用域的情况下才具有实际效果

  • @DependsOn 会在创建此 bean 之前强制创建特定的其他 bean,以及该 bean 通过直接引用表示的任何依赖关系,这通常对单例启动很有帮助。

  • @Primary 是一种机制,用于在注入点级别解决歧义性,如果需要注入单个目标组件,但多个 bean 按类型匹配。

此外,@Bean方法还可以声明限定符注释和@Order值,在注入点解析期间要加以考虑,就像对应组件类上的相应注释一样,但每个Bean定义都可能非常独立(在使用相同bean类的多个定义的情况下)。限定符在初始类型匹配之后缩小候选集;在集合注入点的情况下,顺序值决定已解析元素的顺序(多个目标bean按类型和限定符匹配)。

注意:@Order 值可能会影响注入点的优先级,但是请注意,它们不会影响单例启动顺序,这是由依赖关系和@DependsOn 声明确定的正交关系,如上所述。 同样,由于不能在方法上声明优先级,因此该级别不可用。 它的语义可以通过 @Order 值与 @Primary 结合在每种类型的单个 bean 上进行建模。

@Configuration 类中的 @Bean 方法

通常,@Bean 方法在 @Configuration 类中声明。在这种情况下,bean 方法可以通过直接调用它们来引用同一类中的其他 @Bean 方法。 这确保了 bean 之间的引用是强类型的且可导航的。保证此类所谓的“bean间引用”遵循作用域和AOP语义,就像 getBean()  查找一样。这些是原始“Spring JavaConfig”项目中已知的语义,这些语义要求在运行时将每个此类配置类的 CGLIB 子类化。因此,在此模式下,不得将 @Configuration 类及其工厂方法标记为 final 或 private。例如:

@Configuration
public class AppConfig {

    @Bean
    public FooService fooService() {
        return new FooService(fooRepository());
    }

    @Bean
    public FooRepository fooRepository() {
        return new JdbcFooRepository(dataSource());
    }

    // ...
}

上面实例中,在 fooService()方法中调用了 fooRepository() 方法,用于获取 FooRepository 对象。

@Bean Lite模式

@Bean 方法也可以在未使用 @Configuration 注释的类中声明。例如,可以在 @Component 类中甚至在普通的旧类中声明 bean 方法。在这种情况下,@Bean 方法将以所谓的“精简”模式进行处理。

容器将精简模式下的 Bean 方法视为普通工厂方法(类似于 XML 中的工厂方法声明),并适当地应用了作用域和生命周期回调。在这种情况下,包含类保持不变,并且对于包含类或工厂方法没有异常约束。

与 @Configuration 类中 bean 方法的语义相反,精简模式不支持“ bean间引用”。相反,当一个 @Bean 方法在精简模式下调用另一个 @Bean 方法时,该调用是标准的 Java 方法调用。 Spring 不会通过 CGLIB 代理拦截调用。这类似于内部@Transactional 方法调用,在代理模式下,Spring 不会拦截调用 —— Spring 仅在 AspectJ 模式下会拦截调用。例如:

@Component
public class Calculator {
    public int sum(int a, int b) {
        return a+b;
    }

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

引导

有关更多详细信息,请参见 @Configuration javadoc,包括如何使用 AnnotationConfigApplicationContext 来引导容器。

BeanFactoryPostProcessor-returning @Bean 方法

对于返回 Spring BeanFactoryPostProcessor(BFPP)类型的 @Bean 方法,必须特别注意。 因为 BFPP 对象必须在容器生命周期的早期就实例化,所以它们会干扰 @Configuration 类中的 @Autowired、@Value 和 @PostConstruct 等注解的处理。为了避免这些生命周期问题,请将 BFPP 返回的 @Bean 方法标记为静态。 例如:

@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
    // instantiate, configure and return pspc ...
}

通过将此方法标记为静态,可以在不引起其声明 @Configuration 类实例化的情况下调用该方法,从而避免了上述生命周期冲突。 但是请注意,如上所述,静态 @Bean 方法不会针对范围和 AOP 语义进行增强。 这在 BFPP 情况下可行,因为其他 @Bean 方法通常不引用它们。 提醒一下,将为具有返回类型可分配给 BeanFactoryPostProcessor 的任何非静态 @Bean 方法发出 INFO 级日志消息。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号