likes
comments
collection
share

@Bean 注解

作者站长头像
站长
· 阅读数 25

日积月累,水滴石穿 😄

前言

@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);
    }
}
  • 启动结果

@Bean 注解

u()只被调用了一次。

那什么情况下会被都用两次呢?

  • 如果 Spring 版本在 5.2 以上,需要保证@ConfigurationproxyBeanMethods属性值为 true。

  • @Bean标注的方法是静态的。

结果如下,每调用一次 u() 方法都会产生新的实例。

@Bean 注解

与@Component使用

@Bean 注解 不允许直接调用带有 @Bean 注解的方法。 需要使用依赖注入的方式。

注意点3

ProductServiceImpl的创建移到 UserServiceImpl中。

@Bean
public static ProductServiceImpl p(){
	return new ProductServiceImpl();
}

这时候 ProductServiceImpl不能注入到容器中。这是因为 Spring 做了限制,被 @Bean 注入的Bean,不能在内部使用创建Bean的功能。 比如:@Bean、@Import。


  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。