终于搞懂了 @Configuration 和 @Component 的区别

2023-11-14

一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

理解:调用@Configuration类中的@Bean注解的方法,返回的是同一个示例;而调用@Component类中的@Bean注解的方法,返回的是一个新的实例。

注意:上面说的调用,而不是从spring容器中获取! 见最下面的示例 1 及 示例 2

下面看看实现的细节。

@Configuration 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

从定义来看, @Configuration注解本质上还是@Component,因此 <context:component-scan/> 或者 @ComponentScan 都能处理@Configuration注解的类。

@Configuration标记的类必须符合下面的要求:

  • 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。

  • 配置类不能是final 类(没法动态代理)。

  • 配置注解通常为了通过 @Bean注解生成 Spring 容器管理的类,

  • 配置类必须是非本地的(即不能在方法中声明,不能是 private)。

  • 任何嵌套配置类都必须声明为static。

  • @Bean方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。

@Bean 注解方法执行策略

先给一个简单的示例代码:

@Configuration
public class MyBeanConfig {

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country());
    }

}

相信大多数人第一次看到上面 userInfo() 中调用 country()时,会认为这里的 Country和上面 @Bean方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:

  • @Autowired

  • private Country country;

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

@Component 注解

@Component注解并没有通过 cglib 来代理@Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country

@Component
public class MyBeanConfig {

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country());
    }

}

有些特殊情况下,我们不希望 MyBeanConfig被代理(代理后会变成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293)时,就得用 @Component,这种情况下,上面的写法就需要改成下面这样:

@Component
public class MyBeanConfig {

    @Autowired
    private Country country;

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country);
    }

}

这种方式可以保证使用的同一个 Country 实例。

示例 1:调用@Configuration类中的@Bean注解的方法,返回的是同一个示例

第一个bean类

package com.xl.test.logtest.utils;

public class Child {
 private String name = "the child";

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

第二个bean类

package com.xl.test.logtest.utils;

public class Woman {
 
 private String name = "the woman";
 
 private Child child;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Child getChild() {
  return child;
 }

 public void setChild(Child child) {
  this.child = child;
 }
}

@Configuration

package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
//@Component
public class Human {
 
 @Bean
 public Woman getWomanBean() {
  Woman woman = new Woman();
  woman.setChild(getChildBean()); // 直接调用@Bean注解的方法方法getChildBean()
  return woman;
 }
 
 @Bean
 public Child getChildBean() {
  return new Child();
 }
}

测试类 I

本测试类为一个配置类,这样启动项目是就可以看到测试效果,更加快捷;也可以使用其他方式测试见下面的测试类 II

package com.xl.test.logtest.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Man {
 
 @Autowired
 public Man(Woman wn, Child child) {
  System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  System.out.println(wn.getChild() == child ? "是同一个对象":"不是同一个对象");
 }
}

启动项目,查看输出结果:

测试类 II

package com.xl.test.logtest.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xl.test.logtest.utils.Child;
import com.xl.test.logtest.utils.Woman;

@RestController
public class LogTestController {
 @Autowired
 Woman woman ;
 
 @Autowired
 Child child;
 
 @GetMapping("/log")
 public String log() {
  return woman.getChild() == child ? "是同一个对象":"不是同一个对象";
 }
}

浏览器访问项目,查看结果;输入localhost:8080/log

示例 2 :调用@Component类中的@Bean注解的方法,返回的是一个新的实例。

测试代码,只需要将@Configuration改为@Component即可!其他的均不变

package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Configuration
@Component
public class Human {
 
 @Bean
 public Woman getWomanBean() {
  Woman woman = new Woman();
  woman.setChild(getChildBean()); // 直接调用@Bean注解的方法方法getChildBean()
  return woman;
 }
 
 @Bean
 public Child getChildBean() {
  return new Child();
 }
}

测试 :

控制台和浏览器展示,均符合预期!

作者:爱看老照片

来源:blog.csdn.net/qq_29025955/article/

details/128818957

推荐

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

终于搞懂了 @Configuration 和 @Component 的区别 的相关文章

随机推荐

  • 手撸算法-计算表达式

    牛客原题 描述 请写一个整数计算器 支持加减乘三种运算和括号 示例1 输入 1 2 返回值 3 示例2 输入 2 3 4 5 返回值 10 示例3 输入 3 2 3 4 1 返回值 26 思路 从左向右遍历字符串 1 遇到数字则入栈 注意数
  • C#Winform窗体实现服务端和客户端通信例子(TCP/IP)

    Winform窗体实现服务端和客户端通信的例子 是参考这个地址 http www cnblogs com longwu archive 2011 08 25 2153636 html 进行了一些异常处理 提示信息的补充 还有新增获取本地IP
  • 【docker】docker 实现 的基础

    1 概述 还不懂Docker 一个故事安排的明明白白
  • 大数相加(c++)算法

    有没有想过100位数加100数的数字该如何计算出结果吗 一般计算机是无法直接计算那么大的数字 这个时候我们得模拟我们手算加法的进制过程 如何用代码把它实现 这样子就能实现大数相加了 首先我很感谢汤健同学给我分享的这串代码 我写笔记的初衷是为
  • R 中的with() 函数和 by()函数 的简单使用

    with data expr 函数用于在一个从data构建出的环境中运行R表达式 by data INDICES FUN simplify TRUE 函数用于将data中的数据 按照INDICES里面的内容拆分成若干个小的data fram
  • 提高ubuntu下访问github的速度\加速git clone速度

    这个是真的有用 https blog csdn net hn tzy article details 88903642
  • 实战中的 Promise 和 Future

    上一章介绍了 Future 类型 以及如何用它来编写高可读性 高组合性的异步执行代码 Future 只是整个谜团的一部分 它是一个只读类型 允许你使用它计算得到的值 或者处理计算中出现的错误 但是在这之前 必须得有一种方法把这个值放进去 这
  • Python正则表达式re模块学习遇到的问题

    Python正则表达式处理的组是什么 Python正则表达式处理中的匹配对象是什么 Python匹配对象的groups groupdict和group之间的关系 Python正则表达式re match r a1b2c3 匹配结果为什么是 c
  • Swiper、vue-awesome-swiper 插件使用

    Swiper在PC端和移动端都适用 官方网站 Swiper中文网首页 官方 vue2 配合 swiper5或6版本 vue3 可以使用 swiper8最新版本 一 Swiper插件 Vue React Angular框架都可以使用 1 使用
  • 【计算机组成原理】实验4:存储器读写实验

    实验内容 一 实验原理 存储器是计算机的存储部件 用于存放程序和数据 存储器是计算机信息存储的核心 是计算机必不可少的部件之一 计算机就是按存放在存储器中的程序自动有序不间断地进行工作 本系统从提高存储器存储信息效率的角度设计数据通路 按现
  • wms系统与物联网发展趋势密切相关

    物联网 简称IoT 是指通过各种信息传感器 射频识别技术 全球定位系统 红外感应器 激光扫描器等各种装置与技术 实时采集任何需要监控 连接 互动的物体或过程 采集其声 光 热 电 力学 化学 生物 位置等各种需要的信息 通过各类可能的网络接
  • 海德拉 暴力破解ssh密码

    上一篇博客写到怎么有效地防护ssh密码遭到暴力破解 今天给大家介绍下如何暴力破解ssh密码 作为一名云计算工程师 懂得如何防护比如何攻击更重要 hydra是世界顶级密码破解工具 支持几乎所有协议的在线密码破解 密码能否被破解取决于密码字典是
  • 华为OD机试真题-优雅子数组Python实现【2023.Q1】

    题目内容 如果一个数组中出现次数最多的元素出现大于等于K次 被称为K 优雅数组 k也可以被称为优雅阈值 例如 数组1 2 3 1 2 3 1 它是一个3 优雅数组 因为元素1出现次数大于等于3次 数组1 2 3 1 2就不是一个3 优雅数组
  • MySQL 日期格式化

    本文旨在以最快的速度 提供你需要的 MySQL 日期格式化方案 1 将时间格式化为 YYYY mm dd HH ii ss 格式 我想你要搜的就是这个 哈哈哈 SELECT DATE FORMAT NOW Y m d H i s 效果如图
  • python中dump与dumps的区别

    Python3 JSON模块的使用 参考链接 https docs python org 3 library json html 这里只是介绍最常用的dump dumps和load loads import json 自定义了一个简单的数据
  • flume使用(二):采集远程日志数据到MySql数据库

    本文内容可查看目录 本文内容包含单节点 单agent 和多节点 多agent 采集远程日志 说明 一 环境 linux系统 Centos7 Jdk 1 7 Flume 1 7 0 二 安装 linux中jdk mysql的安装不多赘述 fl
  • Redis清除缓存命令

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 方案1 windows操作系统 进入redis的安装目录 双击redis cli exe 执行 dbsize 执行 flushall 退出 方案2 linux操作系统 进入
  • vue-quill-editor富文本编辑器使用及配置更改

    quill editor支持了常用的功能 但是有2点 需要我们自己定制一下 vue集成quill editor很简单 网上有很多介绍 自行百度下即可 1 图片上传 因为编辑器默认是将图片转成base64存储的 而我们实际开发需要将图片存在自
  • 怎么给PDF签名?来看看这几个方法吧

    年关将至 这几天我所在的部门每个人都很忙碌 都在对今天年尾的申报文件以及明年的商家合同进行处理 今天 领导让我将几份商家合同扫描成PDF电子版本 同时将负责人签名导入文件中 不过由于我之前只接触过扫描文档 并不会在电子文件上导入签名 于是我
  • 终于搞懂了 @Configuration 和 @Component 的区别

    一句话概括就是 Configuration 中所有带 Bean 注解的方法都会被动态代理 因此调用该方法返回的都是同一个实例 理解 调用 Configuration类中的 Bean注解的方法 返回的是同一个示例 而调用 Component类