@Bean 注解指示方法生成由 Spring 容器管理的 Bean。
该注释的属性的名称和语义有意类似于Spring XML模式中的 <bean/> 元素的名称和语义。例如:
@Bean public MyBean myBean() { // instantiate and configure MyBean obj return obj; }
当 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; }
请注意,@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 上进行建模。
通常,@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 方法也可以在未使用 @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 来引导容器。
对于返回 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 级日志消息。