Spring的注解@Bean

@Bean中的autowire属性

现在我们有如下几个方法,模拟一个简单的Spring的bean注入的过程。

AppConfig

@ComponentScan("com.zal")
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

Service

public class OrderService {
}

public class UserService {

    private OrderService orderService;

    public void test() {
        System.out.println(orderService);
    }

    public OrderService getOrderService() {
        return orderService;
    }

    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.test();
    }
}

这时我们如果去运行Test方法的话,控制台输出的肯定是null,因为虽然UserService被当成bean注入了,但是OrderUser并没有被注入容器,所以这时我们输出的是null。

如果我们想要给OrderService进行自动注入的话,通常的做法肯定是给OrderService上加上@Autowired注解。

现在就是说,我们有没有什么方式,不加@Autowired注解,就能实现OrderService的bean注入呢?

有,我们可以使用@Bean注解的autowire属性来进行bean的注入,前提是需要将OrderService装配成bean,也就是加上@Component注解。

@Component
public class OrderService {
}
@ComponentScan("com.zal")
public class AppConfig {
    @Bean(autowire = Autowire.BY_NAME)
    public UserService userService() {
        return new UserService();
    }
}

实现原理:

​ 其实当我们在@Bean注解上使用了autowire属性时,在给UserService的属性OrderService赋值的同时,它不是从spring容器中直接去查找bean,然后进行赋值的。而是通过UserSerivce的get、set方法来进行赋值,首先它会解析UserService的所有的set方法,BY_TYPE就是通过参数类型来判断spring容器中是否有这个bean,如果有就传给set方法,进行赋值,BY_NAME则是通过参数的参数名来进行判断的,非常类似。

​ 这一切都是spring帮我们来实现的。

但是这个方法现在已经过期了,不推荐使用。这是因为这种方法需要实现Service的get、set方法,但是我们也不确定有多少属性需要实现自动注入,我们将所有的属性都生成get、set方法,就显得有些浪费资源,而且这种方式没有直接加注解@Autowire,所以这种方法已经不推荐使用了。

@Bean中的autowireCandidate属性

作用:自动注入的候选者,默认值为true

这里我们先把UserService和OrderService都注入成bean,然后在给UserService中的OrderService属性加上@Autowire注解。

@ComponentScan("com.zal")
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        return new OrderService();
    }
}
public class UserService {

    @Autowired
    private OrderService orderService;
}

显然,这两段代码,如果执行测试类的话,是没有什么问题的,但是如果我们注入多个OrderService呢?根据@Autowired的特性来说,先byType再byName,如果有多个OrderService进行注入,那么spring会自动匹配参数名称相同的那个,但是如果两个参数名称和我们添加了@Autowired注解的OrderService的参数的名称都不一样,那么就会报错。

No qualifying bean of type 'com.zal.service.OrderService' available: expected single matching bean but found 2: orderService,orderService1

报错信息很明显,就是找到了类型相同但是名称都不相同的bean。

这时我们给其中一个bean加上autowireCandidate属性,并且设置其属性为false。

	@Bean(autowireCandidate = false)
    public OrderService orderService1(){
        return new OrderService();
    }

这时再运行测试类,发现就没有什么问题了,也能正常输出。

这就说明@Bean的autowireCandidate表示该bean不能注入给其他bean,不能作为spring的依赖注入的候选者。

在这里插入图片描述

@Bean和@Component

@Bean注解不仅仅只能写在@Configrruation中,还可以写在@Component中。

@Component
public class UserService {

    @Bean
    public OrderService orderService(){
        return new OrderService();
    }
}

@Bean自定义注解

	// 将UserService定义成多例bean
	@Bean
    @Scope(value = "prototype")
    public UserService userService(){
        return new UserService();
    }

如果是一个bean或者两个bean,我们可以这样写,是没有什么问题的,但是我们如果给很多个bean都注册成为多例bean,那么我们就可以自定义一个注解,来实现这样的功能:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Bean
@Scope(value = "prototype")
public @interface ProtoTypeBean {
}

如果我们想要实现多例bean的注册,只需要添加@ProtoTypeBean这一个注解就行了。

@Bean与@Configuration注解之间的关系

@ComponentScan("com.zal")
public class AppConfig {

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        System.out.println(userService());
        System.out.println(userService());
        return new OrderService();
    }
}

如果我们不加@Configuration注解的话,我们运行测试类会发现,这两个打印的userService是不相同的,而且与Spring注册的bean的地址也是不相同的,那么这就会出现问题了,因为我们的UserSercvice是单例bean,全局只能存在一个。而如果我们加上了@Configuration注解,所有的userService地址又都相同了,那么这是为什么呢?

如果我们不加@Configuration注解时,相当于每次创建UserService使用的就普通的AppConfig类,每次创建都会返回一个新的UserService,所以每次返回的对象都不一样。

普通的AppConfigcom.zal.AppConfig@4524411f
代理对象AppConfigcom.zal.AppConfig$$EnhancerBySpringCGLIB$$793ecb84@341b80b2(CGLIB代理对象)

但是如果加了@Configuration之后,每次返回的对象其实是一个代理对象。每次调用userService()的时候代理对象先去判断是否创建了相应的bean,如果没有,就创建并且存储到spring的容器中,如果有,就直接从容器中取出返回即可。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。\[1\]被标记为@Bean的方法BeanDefinition会被注册到Spring的工厂中,当Spring的ApplicationContext进行刷新时,这个对象就会被实例化并交由Spring管理。\[2\]通常情况下,@Bean注解与@Configuration一起使用,用于配置和初始化一个由Spring IOC容器管理的新对象。\[3\] #### 引用[.reference_title] - *1* [Spring之@Bean注解详解](https://blog.csdn.net/qq1309664161/article/details/119082339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [Spring的@Bean注解原理详解](https://blog.csdn.net/qq_34203492/article/details/126505543)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *3* [Spring——@Bean注解](https://blog.csdn.net/b15735105314/article/details/113766385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值