日积月累,水滴石穿 😄
前言
@Bean
是一个应用在方法(还可以用在注解上)上的注解,被 @Bean
标注的方法会生成一个由 Spring 容器管理的 bean。
@Bean 与 xml 文件中的 <bean/>
标签等同,@Bean
需要和@Component
或者 @Configuration
一同使用,通常是和 @Configuration
(可以想一想),如下:
@Configuration
public class BeanConfig {
@Bean
public UserServiceImpl u(){
return new UserServiceImpl();
}
}
源码定义
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
// name的别名,在无其他属性的情况下,可以如下写法指定 beanName
// @Bean("u")
@AliasFor("name")
String[] value() default {};
//指定 beanName,如果不指定时,采用方法名称为 beanName
//如果指定多个,第一个名称为 beanName,其余为 别名
@AliasFor("value")
String[] name() default {};
/**
* 装配的方式,有三个选项
* Autowire.NO (默认设置)
* Autowire.BY_NAME:根据 beanName
* Autowire.BY_TYPE:根据Class类型
* 一般不设置,采用默认即可,而且该属性已经被放弃,不介意使用
*/
@Deprecated
Autowire autowire() default Autowire.NO;
//该 bean 是否可以自动装配到其他 bean 中,默认为 true
// 如果为 false,代表其他 bean,不能进行注入该bean
boolean autowireCandidate() default true;
// bean 的初始化方法, 直接指定方法名称,不用带括号
// 默认值为 "",表示不调用初始化方法
String initMethod() default "";
//指定销毁方法
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
举例
value
public class UserServiceImpl {
}
@Bean({"u1","u2"}) // beanName 为 u1,别名为u2
public UserServiceImpl u(){
return new UserServiceImpl();
}
autowireCandidate
autowireCandidate
属性设置为 false,在 ProductServiceImpl
类中使用 @Autowired
进行注入,这时会报错。
@Bean(name = {"u1","u2"},autowireCandidate = false)
public UserServiceImpl u(){
return new UserServiceImpl();
}
@Component
public class ProductServiceImpl {
@Autowired
private UserServiceImpl userService;
}
initMethod
指定初始化方法,initMethod2
。
@Bean(name = {"u1","u2"},initMethod = "initMethod2")
public UserServiceImpl u(){
return new UserServiceImpl();
}
在 UserServiceImpl
类中添加 initMethod2
方法。
public void initMethod2(){
System.out.println("UserServiceImpl = initMethod2");
}
destroyMethod
指定销毁方法,destroyMethod2
。
@Bean(name = {"u1","u2"},initMethod = "initMethod2",destroyMethod = "destroyMethod2")
在 UserServiceImpl
类中添加 destroyMethod2
方法。
public void destroyMethod2(){
System.out.println("UserServiceImpl = destroyMethod2");
}
在容器停止时,会调用 destroyMethod2
方法。不过,只是有单例 bean 才会调用该方法,如果是其他作用域,不会调用该方法。如果你在 UserServiceImpl
中添加了名为“close”或“shutdown”的公共、无参数方法,即使你不指定 destroyMethod
属性,也会被调用,如果想禁用,请将destroyMethod
属性值设置为 ""。
@ComponentScan(basePackages = "com.cxyxj.beandemo")
public class AppMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppMain.class);
// 打印 bean 名称
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
//根据 beanName 获得别名
String[] u1s = context.getAliases("u1");
System.out.println("别名" + Arrays.asList(u1s));
// 关闭容器
context.close();
}
}
注意点
注意点 1
发现@Bean
的源码定义中并没有环境激活、懒加载、作用域、是否首选Bean、依赖的设置,它应该和@Profile
、@Lazy
@Scope
、@DependsOn
、@Primary
一起使用来声明。
@Profile
:指定Bean在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个Bean。@Scope
将 bean 的范围从单例更改为指定范围。@Lazy
只有在默认单例范围的情况下才有实际效果。@DependsOn
强制在创建此 bean 之前创建特定的其他 bean。@Primary
指示当多个候选者有资格自动装配依赖项时,应优先考虑该bean
注意点2
开篇讲到@Bean
需要和@Component
或者 @Configuration
一同使用,通常使用 @Configuration
。这两者之间有什么区别呢?
与@Configuration使用
@Configuration
public class BeanConfig {
@Bean(name = {"u1","u2"})
public UserServiceImpl u(){
UserServiceImpl userService = new UserServiceImpl();
System.out.println("userService = " + userService);
return userService;
}
@Bean
public ProductServiceImpl p(){
u();
return new ProductServiceImpl();
}
}
- 启动类
@ComponentScan(basePackages = "com.cxyxj.beandemo")
public class AppMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppMain.class);
}
}
- 启动结果
u()
只被调用了一次。
那什么情况下会被都用两次呢?
- 如果 Spring 版本在 5.2 以上,需要保证
@Configuration
的proxyBeanMethods
属性值为 true。 - 被
@Bean
标注的方法是静态的。
结果如下,每调用一次 u()
方法都会产生新的实例。
与@Component使用
不允许直接调用带有 @Bean
注解的方法。 需要使用依赖注入的方式。
注意点3
将 ProductServiceImpl
的创建移到 UserServiceImpl
中。
@Bean
public static ProductServiceImpl p(){
return new ProductServiceImpl();
}
这时候 ProductServiceImpl
不能注入到容器中。这是因为 Spring 做了限制,被 @Bean 注入的Bean,不能在内部使用创建Bean的功能。 比如:@Bean、@Import。
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。