面试官:@Configuration 和 @Component 注解的区别?大部分人都会答错! ...

打印 上一主题 下一主题

主题 711|帖子 711|积分 2133

一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
理解:调用@Configuration类中的@Bean注解的方法,返回的是同一个示例;而调用@Component类中的@Bean注解的方法,返回的是一个新的实例。
注意:上面说的调用,而不是从spring容器中获取! 见最下面的示例 1 及 示例 2
下面看看实现的细节。
@Configuration 注解:
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Configuration {
  6.     String value() default "";
  7. }
复制代码
从定义来看, @Configuration注解本质上还是@Component,因此  或者 @ComponentScan 都能处理@Configuration注解的类。
@Configuration标记的类必须符合下面的要求:

  • 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
  • 配置类不能是final 类(没法动态代理)。
  • 配置注解通常为了通过 @Bean注解生成 Spring 容器管理的类,
  • 配置类必须是非本地的(即不能在方法中声明,不能是 private)。
  • 任何嵌套配置类都必须声明为static。
  • @Bean方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。
@Bean 注解方法执行策略

推荐一个开源免费的 Spring Boot 最全教程:
https://github.com/javastacks/spring-boot-best-practice
先给一个简单的示例代码:
  1. @Configuration
  2. public class MyBeanConfig {
  3.     @Bean
  4.     public Country country(){
  5.         return new Country();
  6.     }
  7.     @Bean
  8.     public UserInfo userInfo(){
  9.         return new UserInfo(country());
  10.     }
  11. }
复制代码
相信大多数人第一次看到上面 userInfo() 中调用 country()时,会认为这里的 Country和上面 @Bean方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:

  • @Autowired
  • private Country country;
实际上不需要这么做(后面会给出需要这样做的场景),直接调用country() 方法返回的是同一个实例。
@Component 注解

@Component注解并没有通过 cglib 来代理@Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country。
  1. @Component
  2. public class MyBeanConfig {
  3.     @Bean
  4.     public Country country(){
  5.         return new Country();
  6.     }
  7.     @Bean
  8.     public UserInfo userInfo(){
  9.         return new UserInfo(country());
  10.     }
  11. }
复制代码
有些特殊情况下,我们不希望 MyBeanConfig被代理(代理后会变成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293)时,就得用 @Component,这种情况下,上面的写法就需要改成下面这样:
  1. @Component
  2. public class MyBeanConfig {
  3.     @Autowired
  4.     private Country country;
  5.     @Bean
  6.     public Country country(){
  7.         return new Country();
  8.     }
  9.     @Bean
  10.     public UserInfo userInfo(){
  11.         return new UserInfo(country);
  12.     }
  13. }
复制代码
这种方式可以保证使用的同一个 Country 实例。
示例 1:调用@Configuration类中的@Bean注解的方法,返回的是同一个示例
第一个bean类
  1. package com.xl.test.logtest.utils;
  2. public class Child {
  3. private String name = "the child";
  4. public String getName() {
  5.   return name;
  6. }
  7. public void setName(String name) {
  8.   this.name = name;
  9. }
  10. }
复制代码
第二个bean类
  1. package com.xl.test.logtest.utils;
  2. public class Woman {
  3. private String name = "the woman";
  4. private Child child;
  5. public String getName() {
  6.   return name;
  7. }
  8. public void setName(String name) {
  9.   this.name = name;
  10. }
  11. public Child getChild() {
  12.   return child;
  13. }
  14. public void setChild(Child child) {
  15.   this.child = child;
  16. }
  17. }
复制代码
@Configuration类
  1. package com.xl.test.logtest.utils;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.stereotype.Component;
  5. @Configuration
  6. //@Component
  7. public class Human {
  8. @Bean
  9. public Woman getWomanBean() {
  10.   Woman woman = new Woman();
  11.   woman.setChild(getChildBean()); // 直接调用@Bean注解的方法方法getChildBean()
  12.   return woman;
  13. }
  14. @Bean
  15. public Child getChildBean() {
  16.   return new Child();
  17. }
  18. }
复制代码
测试类 I
本测试类为一个配置类,这样启动项目是就可以看到测试效果,更加快捷;也可以使用其他方式测试见下面的测试类 II
  1. package com.xl.test.logtest.utils;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. public class Man {
  6. @Autowired
  7. public Man(Woman wn, Child child) {
  8.   System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  9.   System.out.println(wn.getChild() == child ? "是同一个对象":"不是同一个对象");
  10. }
  11. }
复制代码
启动项目,查看输出结果:

测试类 II
  1. package com.xl.test.logtest.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import com.xl.test.logtest.utils.Child;
  6. import com.xl.test.logtest.utils.Woman;
  7. @RestController
  8. public class LogTestController {
  9. @Autowired
  10. Woman woman ;
  11. @Autowired
  12. Child child;
  13. @GetMapping("/log")
  14. public String log() {
  15.   return woman.getChild() == child ? "是同一个对象":"不是同一个对象";
  16. }
  17. }
复制代码
浏览器访问项目,查看结果;输入localhost:8080/log

示例 2 :调用@Component类中的@Bean注解的方法,返回的是一个新的实例。
测试代码,只需要将@Configuration改为@Component即可!其他的均不变
  1. package com.xl.test.logtest.utils;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.stereotype.Component;
  5. //@Configuration
  6. @Component
  7. public class Human {
  8. @Bean
  9. public Woman getWomanBean() {
  10.   Woman woman = new Woman();
  11.   woman.setChild(getChildBean()); // 直接调用@Bean注解的方法方法getChildBean()
  12.   return woman;
  13. }
  14. @Bean
  15. public Child getChildBean() {
  16.   return new Child();
  17. }
  18. }
复制代码
测试 :

控制台和浏览器展示,均符合预期!
原文:blog.csdn.net/qq_29025955/article/details/128818957
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表