@Bean使用场景

一、简单介绍

翻看Spring的源码时,发现@Bean注解的源码上标注了Since: 3.0,也就是说,@Bean注解是Spring从3.0版本开始提供的源码。@Bean注解可以标注在方法上,将当前方法的返回值注入到IOC容器中,也可以标注到注解上,作为元注解使用。

二、注解说明

@Bean注解可以说是使用Spring开发应用程序时,使用的比较多的一个注解,尤其是基于SpringBoot开发应用程序时,@Bean注解使用的就很多了。@Bean注解可以标注到方法上,将当前方法的返回值注入到IOC容器中。@Bean注解也可以标注到注解上,作为元注解使用。

1. 注解源码

/**
 * @author Rod Johnson
 * @author Costin Leau
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.0
 */
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
 //Since: 4.3.3
 @AliasFor("name")
 String[] value() default {};

 @AliasFor("value")
 String[] name() default {};
 
 //Since: 5.1
 boolean autowireCandidate() default true;

 String initMethod() default "";

 String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

从@Bean源码的注释可以看出,@Bean注解是Spring从3.0版本开始提供的注解,注解中各个属性的含义如下所示:

  • name:String[]数组类型,指定注入到IOC容器中的Bean的名称,可以指定多个名称。如果不指定name属性和value属性的值,则注入到IOC容器中的Bean的名称默认是方法的名称。
  • value:String[]数组类型,从Spring 4.3.3版本开始提供的@Bean的属性,作用与name属性相同。
  • autowireCandidate:boolean类型,表示是否支持自动按照类型注入到其他的Bean中。此属性会影响@Autowired注解,不会响应@Resource注解,默认为true,表示支持自动按照类型注入到其他的Bean中。
  • initMethod:指定初始化的方法。
  • destroyMethod:指定销毁的方法。

2. 注解使用场景

在使用Spring的注解开发应用程序时,如果是我们自己开发的类,可以在类上标注@Component注解(也可以是@Repository、@Service、@Controller等注解),将类注入到IOC容器中。但是,有时很多类不是我们自己写的,而是依赖的第三方的类库,此时就无法在类上标注@Component等注解了,此时就需要使用@Bean注解将其注入到IOC容器中。

也就是说,@Bean注解适用于将第三方提供的类注入到IOC容器中的场景。使用@Bean注解创建的Bean对象默认是单实例Bean对象。

三、使用案例

1. 案例描述

使用@Bean注解将User类的对象注入到IOC容器中,打印初始化和销毁方法,并验证使用@Bean注解创建的Bean对象默认是单实例Bean。

2. 案例实现

使用@Bean注解可以将类对象注入到IOC容器中,并且@Bean注解的initMethod属性可以指定初始化的方法,destroyMethod属性可以指定销毁的方法。当IOC容器对Bean对象进行初始化时,会执行@Bean注解的initMethod属性指定的方法,当IOC容器在关闭时,会执行@Bean注解的destroyMethod属性指定的方法,触发销毁方法的逻辑。

注意:上述逻辑仅限于注入到IOC容器中的单例Bean对象。如果是单实例Bean,则IOC容器启动时,就会创建Bean对象,IOC容器关闭时,销毁Bean对象。如果是多实例Bean,IOC容器在启动时,不会创建Bean对象,在每次从IOC容器中获取Bean对象时,都会创建新的Bean对象返回,IOC容器关闭时,也不会销毁对象。也就是说,如果是多实例Bean,IOC容器不会管理Bean对象。

本节,就以单实例Bean为例来实现案例程序,具体的实现步骤如下所示:

(1)新建注入到IOC容器中的User类

public class User {

    private final Logger logger = LoggerFactory.getLogger(User.class);

    public User(){
        logger.info("执行构造方法...");
    }

    public void init(){
        logger.info("执行初始化方法...");
    }

    public void destroy(){
        logger.info("执行销毁方法...");
    }
}

IOC容器启动时,会创建User类的对象并调用init()方法进行初始化。IOC容器关闭时,会销毁User类的对象,并调用destroy()方法执行销毁逻辑。

可以看到,User类的实现比较简单,提供了一个无参构造方法,一个表示初始化的init()方法和一个表示销毁的destroy()方法,每个方法中都打印了相应的日志来说明调用了对应的方法。

(2)创建Spring的配置类BeanConfig

@Configuration
@ComponentScan(basePackages = "com.lwk.demo.spring.annocation.bean")
public class BeanConfig {
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public User user(){
        return new User();
    }
}

可以看到,在BeanConfig类上标注了@Configuration注解,说明BeanConfig类是一个Spring的配置类,并且在BeanConfig类上标注了@ComponentScan注解,指定要扫描的包为com.lwk.demo.spring.annocation.bean。

在BeanConfig类中定义了一个user()方法,返回一个User对象。在user()方法上标注了@Bean注解,并通过initMethod属性指定的初始化方法为User类的init()方法,通过destroyMethod属性指定的销毁方法为User类的destroy()方法。

(3)创建案例测试类BeanTest

public class BeanTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(BeanTest.class);
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        LOGGER.info("IOC容器启动完成....");
        User user1 = context.getBean(User.class);
        User user2 = context.getBean(User.class);
        LOGGER.info("user1是否等于user2===>>>{}", (user1 == user2));
        context.close();
    }
}

可以看到,在BeanTest类中,通过BeanConfig配置类创建了IOC容器,从IOC容器中两次获取User对象,分别赋值给user1和user2,打印user1是否等于user2的日志,并关闭IOC容器。

3. 案例测试

运行BeanTest类的main()方法,输出的日志信息如下所示:

15:56:57.404 [main] INFO com.lwk.demo.spring.annocation.bean.entity.User - 执行构造方法...
15:56:57.405 [main] INFO com.lwk.demo.spring.annocation.bean.entity.User - 执行初始化方法...
15:56:57.431 [main] INFO com.lwk.demo.spring.annocation.bean.BeanTest - IOC容器启动完成....
15:56:57.432 [main] INFO com.lwk.demo.spring.annocation.bean.BeanTest - user1是否等于user2===>>>true
15:56:57.437 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@41975e01, started on Thu Mar 09 15:56:57 CST 2023
15:56:57.438 [main] INFO com.lwk.demo.spring.annocation.bean.entity.User - 执行销毁方法...

可以看到,IOC容器在启动时,就创建了User对象,并调用了@Bean注解的initMethod方法指定的初始化方法,IOC容器在关闭时,调用@Bean注解的destroyMethod属性指定的销毁方法。并且通过打印的user1是否等于user2的日志为true,可以说明使用@Bean注解默认创建的Bean对象是单实例Bean,每个类在IOC容器中只会存在一个单实例Bean对象。

四、@Bean中的autowireCandidate属性

autowireCandidate:boolean类型,表示是否支持自动按照类型注入到其他的Bean中。此属性会影响@Autowired注解,不会响应@Resource注解,默认为true,表示支持自动按照类型注入到其他的Bean中。

1. autowireCandidate影响@Autowired

@Configuration
public class SpringConfiguration {

    /**
     * 创建一个数据源对象
     * @return
     */
    @Bean(value="dataSource", autowireCandidate = false)
    public DataSource createDataSource(){
        return dataSource;
    }

    /**
     * 创建JdbcTemplate对象
     * @return
     */
     // autowireCandidate = false,此时DataSource不会按照类型注入
    @MyBean
    public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
    
}
public class SpringBeanTest {

    public static void main(String[] args) {
        // 创建容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        // 获取JdbcTemplate对象
        JdbcTemplate jdbcTemplate = ac.getBean("createJdbcTemplate", JdbcTemplate.class);
        // 输出jdbcTemplate中的数据源
        System.out.println(jdbcTemplate);
    }
}

autowireCandidate = false,DataSource没有注入到JdbcTemplate中,报错

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1654)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
	... 14 more

2. autowireCandidate不影响@Resource

@Configuration
public class SpringConfiguration {

    @Resource(name="dataSource")
    private DataSource dataSource;

    /**
     * 创建一个数据源对象
     * @return
     */
    @Bean(value="dataSource",autowireCandidate = false)
    public DataSource createDataSource(){
        return dataSource;
    }

    @Bean
    public JdbcTemplate createJdbcTemplate(){
        System.out.println("创建了JdbcTemplate");
        return new JdbcTemplate(dataSource);
    }

}
public class SpringBeanTest {

    public static void main(String[] args) {
        // 创建容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        // 获取JdbcTemplate对象
        JdbcTemplate jdbcTemplate = ac.getBean("createJdbcTemplate", JdbcTemplate.class);
        // 输出jdbcTemplate中的数据源
        System.out.println(jdbcTemplate);
    }
}

打印日志,注入成功

创建了JdbcTemplate
org.springframework.jdbc.core.JdbcTemplate@c8e4bb0

五、@Bean注解的使用细节

1. 使用默认bean的名称

当@Bean不指定name和value属性时,默认的bean名称是方法名。
如果同时配置了方法重载的bean,则注入成功的是有参的bean。

@Configuration
public class SpringConfiguration {

	@Resource(name="dataSource")
    private DataSource dataSource;

    /**
     * 创建一个数据源对象
     * @return
     */
    @Bean(value="dataSource",autowireCandidate = true)
    public DataSource createDataSource(){
        return dataSource;
    }

	@Bean
    public JdbcTemplate createJdbcTemplate(){
        System.out.println("创建了无参的JdbcTemplate");
        return new JdbcTemplate(dataSource);
    }
    
    @Bean
    public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
        System.out.println("创建了有参的JdbcTemplate");
        return new JdbcTemplate(dataSource);
    }

}
public class SpringBeanTest {

    public static void main(String[] args) {
        //创建容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        //获取JdbcTemplate对象
        JdbcTemplate jdbcTemplate = ac.getBean("createJdbcTemplate", JdbcTemplate.class);
        //输出jdbcTemplate中的数据源
        System.out.println(jdbcTemplate);
    }
}

有方法重载的bean时,获取的是有参的bean

创建了有参的JdbcTemplate
org.springframework.jdbc.core.JdbcTemplate@667a738

2. @Bean作为元注解使用

自定义注解,将@Bean作为元注解

Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Bean
public @interface MyBean {
}
@Configuration
public class SpringConfiguration {

    @Resource(name="dataSource")
    private DataSource dataSource;

    /**
     * 创建一个数据源对象
     * @return
     */
    @Bean(value="dataSource",autowireCandidate = true)
    public DataSource createDataSource(){
        return dataSource;
    }

    /**
     * 创建JdbcTemplate对象
     * @return
     */
    @MyBean
    public JdbcTemplate createJdbcTemplate(){
        return new JdbcTemplate(dataSource);
    }
}
public class SpringBeanTest {

    public static void main(String[] args) {
        //创建容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        //获取JdbcTemplate对象
        JdbcTemplate jdbcTemplate = ac.getBean("createJdbcTemplate", JdbcTemplate.class);
        //输出jdbcTemplate中的数据源
        System.out.println(jdbcTemplate);
    }
}

获取成功

org.springframework.jdbc.core.JdbcTemplate@7bb58ca3
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值