如何实现 Spring 的 @Component 注解

本文详细介绍了如何实现Spring框架中的@Component注解,包括读取XML配置文件获取扫描包路径,扫描包路径下的类并读取注解,以及根据注解创建BeanDefinition。文章通过ASM框架读取字节码,解析注解信息,并提供了ClassPathBeanDefinitionScanner类用于扫描和注册BeanDefinition到BeanFactory。下篇将介绍@Autowried注解的实现。
摘要由CSDN通过智能技术生成

前言

前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入、 如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文件方式的实现,从 JDK 5 版本开始 Java 引入了注解支持,带来了极大的便利,Sprinng 也从 2.5 版本开始支持注解方式,使用注解方式我们只需加上相应的注解即可,不再需要去编写繁琐的 XML 配置文件,深受广大 Java 编程人员的喜爱。接下来一起看看如何实现 Spring 框架中最常用的两个注解(@Component、@Autowired),由于涉及到的内容比较多,会分为两篇文章进行介绍,本文先来介绍上半部分 — 如何实现 @Component 注解。

   

实现步骤拆分

本文实现的注解虽然说不用再配置 XML 文件,但是有点需要明确的是指定扫描 Bean 的包还使用 XML 文件的方式配置的,只是指定 Bean 不再使用配置文件的方式。有前面两篇文章的基础后实现 @Component 注解主要分成以下几个步骤:

  1. 读取 XML 配置文件,解析出需要扫描的包路径

  2. 对解析后的包路径进行扫描然后读取标有 @Component 注解的类,创建出对应的 BeanDefinition

  3. 根据创建出来的 BeanDefinition 创建对应的 Bean 实例

下面我们一步步来实现这几个步骤,最后去实现 @Component 注解:

   

读取 XML 配置文件,解析出需要扫描的包路径

假设有如下的 XML 配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.e3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/beans/spring-context.xsd">


    <context:scann-package base-package="cn.mghio.service.version4,cn.mghio.dao.version4" />


</beans>

我们期望的结果是解析出来的扫描包路径为:cn.mghio.service.version4、cn.mghio.dao.version4 。如果有仔细有了前面的文章后,这个其实就比较简单了,只需要修改读取 XML 配置文件的类 XmlBeanDefinitionReader 中的 loadBeanDefinition(Resource resource) 方法,判断当前的 namespace 是否为 context 即可,修改该方法如下:

public void loadBeanDefinition(Resource resource) {
  try (InputStream is = resource.getInputStream()) {
    SAXReader saxReader = new SAXReader();
    Document document = saxReader.read(is);
    Element root = document.getRootElement();  // <beans>
    Iterator<Element> iterator = root.elementIterator();
    while (iterator.hasNext()) {
      Element element = iterator.next();
      String namespaceUri = element.getNamespaceURI();
      if (this.isDefaultNamespace(namespaceUri)) {  // beans
        parseDefaultElement(element);
      } else if (this.isContextNamespace(namespaceUri)) {  // context
        parseComponentElement(element);
      }
    }
  } catch (DocumentException | IOException e) {
    throw new BeanDefinitionException("IOException parsing XML document:" + resource, e);
  }
}


private void parseComponentElement(Element element) {
  // 1. 从 XML 配置文件中获取需要的扫描的包路径
  String basePackages = element.attributeValue(BASE_PACKAGE_ATTRIBUTE);
  // TODO 2. 对包路径进行扫描然后读取标有 @Component 注解的类,创建出对应的 BeanDefinition
  ...
    
}


private boolean isContextNamespace(String namespaceUri) {
  // CONTEXT_NAMESPACE_URI = http://www.springframework.org/schema/context
  return (StringUtils.hasLength(namespaceUri) && CONTEXT_NAMESPACE_URI.equals(namespaceUri));
}


private boolean isDefaultNamespace(String namespaceUri) {
  // BEAN_NAMESPACE_URI = http://www.springframework.org/schema/beans
  return (StringUtils.hasLength(namespaceUri) && BEAN_NAMESPACE_URI.equals(namespaceUri));
}

第一个步骤就已经完成了,其实相对来说还是比较简单的,接下来看看第二步要如何实现。

   

对解析后的包路径进行扫描然后读取标有 @Component 注解的类,创建出对应的 BeanDefinition

第二步是整个实现步骤中最为复杂和比较麻烦的一步,当面对一个任务比较复杂而且比较大时,可以对其进行适当的拆分为几个小步骤分别去实现,这里可以其再次拆分为如下几个小步骤:

  1. 扫描包路径下的字节码(.class )文件并转换为一个个 Resource 对象(其对于 Spring 框架来说是一种资源,在 Spring 中资源统一抽象为 Resource ,这里的字节码文件具体为 FileSystemResource)

  2. 读取转换好的 Resource 中的 @Component 注解

  3. 根据读取到的 @Component 注解信息创建出对应的 BeanDefintion

   

① 扫描包路径下的字节码(.class )文件并转换为一个个 Resource 对象(其对于 Spring 框架来说是一种资源,在 Spring 中资源统一抽象为 Resource ,这里的字节码文件具体为 FileSystemResource)

第一小步主要是实现从一个指定的包路径下获取该包路径下对应的字节码文件并将其转化为 Resource 对象,将该类命名为 PackageResourceLoader,其提供一个主要方法是 Resource[] getResources(String basePackage) 用来将一个给定的包路径下的字节码文件转换为 Resource 数组,实现如下:

public class PackageResourceLoader {
  
    ...
  
    public Resource[] getResources(String basePackage) {
        Assert.notNull(basePackage, "basePackage must not be null");
        String location = ClassUtils.convertClassNameToResourcePath(basePackage);
        ClassLoader classLoader = getClassLoader();
        URL url = classLoader.getResource(location);
        Assert.notNull(url, "URL must not be null");
        File rootDir = new File(url.getFile());


        Set<File> matchingFile = retrieveMatchingFiles(rootDir);
        Resource[] result = new Resource[matchingFile.size()];
        int i = 0;
        for (File file : matchingFile) {
            result[i++] = new FileSystemResource(file);
        }
        return result;
    }


    private Set<File> retrieveMatchingFiles(File rootDir) {
        if (!rootDir.exists() || !rootDir.isDirectory() || !rootDir.canRead()) {
            return Collections.emptySet();
        }
        Set<File> result = new LinkedHashSet<>(8);
        doRetrieveMatchingFiles(rootDir, result);
        return result;
    }


    private void doRetrieveMatchingFiles(File dir, Set<File> result) {
        File[] dirContents = dir.listFiles();
        if (dirContents == null) {
            return;
        }


        for (File content : dirContents) {
            if (!content.isDirectory()) {
                result.add(content);
                continue;
            }
            if (content.canRead()) {
                doRetrieveMatchingFiles(content, result);
            }
        }
    }
  
  ...


}

上面的第一小步至此已经完成了,

最低0.47元/天 解锁文章
如何实现一个简易版的 Spring - 如何实现 @Component 注解
Meldoy_mgh的博客
02-14 485
前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入、如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文件方式的实现,从 JDK 5 版本开始 Java 引入了注解支持,带来了极大的便利,Sprinng 也从 2.5 版本开始支持注解方式,使用注解方式我们只需加上相应的注解即可,不再需要去编写繁琐的 XML 配置文件,深受广大 Java 编程人员的喜爱。接下来一起看看如何实现 Spring 框架中最常用的两个注解
spring注解(三)Component
热门推荐
我的山水
03-21 3万+
上篇文章中比较详细的介绍了依赖注入的相关注解,尤其是@Autowired。但是我们对bean的定义声明还是放在xml的配置文件中。Spring当然提供了机制可以自动的扫描类路径,自动的向容器注册BeanDefinition。这就是Bean级别的注解。以上机制称为类路径扫描(clsspath-sacn),它是有相关注解(如@Component @Named @Bean)和beanFactoryPos
Spring @Component注解
Alecor的博客
10-09 494
基本的就不一一介绍了,直接进入主题 @controller 控制器(注入服务) @service 服务(注入dao) @repository dao(实现dao访问) @component (把普通pojo实例化到spring容器中,相当于配置文件中的 " &lt;Bean id ="" class ="" / &gt; ") @Component,@Service,@Con...
spring @component注解
weixin_43060102的博客
10-11 1384
1、@controller 控制器(注入服务) 2、@service 服务(注入dao) 3、@repository dao(实现dao访问) 4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的) @Component: 定义Spring管理Bean @Repository: @Component扩展,被@Repository注解的POJO类表示DAO层实现,...
spring的@component注解
会飞的冰箱的博客
09-15 1852
@componentspring中的一个注解,它的用就是实现bean的注入,@component取代。在探究@component前先了解一下常用的注解注解本质上就是一个类,开发中我们可以使用注解 取代 xml配置文件。 @controller 控制器(注入服务) 用于标注控制层,相当于struts中的action层 @service 服务(注入dao) 用于标注服务层,主要用来进行业务的逻辑处理 @repository(实现dao访问) 用于标注数据访问层,也可以说用于标注数据访问组件,即
spring @Component注解原理解析
08-25
Spring 框架中 @Component 注解用于标注 Bean 的一种方式,通过使用 @Component 注解Spring 框架可以自动将该类纳入到容器中管理,从而实现了依赖注入和控制反转。 @Component 注解用是将普通的 POJO 对象...
Spring @Bean注解配置及使用方法解析
08-19
在使用 @Bean 注解时,可以与 @Component 或 @Configuration 一起使用,用于注册 Bean 实例到 Spring 容器中。例如: ```java @Configuration public class MyConfigration { @Bean public User user() { return...
面试官:Spring @bean 和 @component 注解有什么区别?
m0_71777195的博客
11-27 505
名称可以通过 name 属性指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名,当注解写在 setter 方法上时,默认取属性名进行装配。以上简单介绍了几种 Spring 中的几个注解及代码示例,就我个人而言,均是平时用到且不容易理解的几个,或者容易忽略的几个。如果不指定,则自动按照 byName 方式装配,如果没有匹配,则回退一个原始类型进行匹配,如果匹配则自动装配。用于全局异常的处理。如果指定 name 属性,则从容器中查找名称匹配的 bean 装配,找不到则抛出异常;
Spring Boot技术知识点:如何深入理解@Component注解
06-18
使用`@Component`注解的类可以通过`@Autowired`注解实现依赖注入。Spring会自动寻找类型匹配的bean,并将其注入到需要的地方,简化了代码的编写。 6. **别名和`@Qualifier`** 如果有多个相同类型的bean,可以使用...
Spring : @Component注解
九师兄
01-19 682
1.美图 2.概述 3.源码 @Target(ElementType.TYPE) //该注解一般用于添加在方法上 @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface Component { /** * 组件对应的名字(获取组件的时候可以通过类型获取,也可以通过名字获取) ...
Spring @Component注解详解
qq_25073223的博客
11-02 594
Spring @Component注解详解
spring】@Component注解学习
一个写了10年bug的程序员日常笔记。
03-22 2528
ComponentSpring 框架中的一个注解用于将一个类标记为 Spring 上下文中的一个组件。当一个类被标记为 @Component 时,Spring 容器会在启动时自动扫描并实例化这个类,并将其注册到 Spring 上下文中。@Component 注解可以用于任何类,包括控制器、服务、DAO 等。当一个类被标记为 @Component 时,它就成为了 Spring 上下文中的一个 bean,可以被其他 bean 通过依赖注入的方式使用。
spring注解@Component、@Bean,@Autowire一遍搞定
健康平安的活着的专栏
08-28 9786
spring注解用 1.1 用 1.@Component注解表明一个类会为组件类,并告知Spring要为这个类创建bean,使用 @Component注解在一个类上,表示将此类标记为Spring容器中的一个Bean。(相当于创建对象) 2.@Bean是将组件注册到Bean,让IOC容器知道这个组件存在。(相当于创建对象) 3.@Autowire:是组件和组件相互调用的时候,自动从ioc中取出来需要用的组件。(调用对象) 比如Service,Control...
从理论到实战:Spring框架@Component注解的全面解读
最新发布
万猫学社
05-19 1162
在使用Spring框架的主要功能时,我们无法避开一个重要的角色——@Component注解。如果你把Spring框架比一部精妙的机械表,那么@Component注解就好比是其中的一个小小的齿轮,虽然看似不起眼,但却对整个系统的运行起着至关重要的用。Spring框架的核心功能就是“控制反转”(Inversion of Control,简称IoC)和“依赖注入”(Dependency Injection,简称DI)。
Spring注解必知必会】深度解析@Component注解实现原理
m0_71777195的博客
08-16 5383
想必@Component注解大家一直在使用,只要类上加上它,就可以被Spring容器管理,那大家有想过它是怎么实现的吗?本篇文章就带领到家揭秘。用来标记的类是一个“组件”或者说是一个Bean,Spring会自动扫描标记@Component注解的类为一个Spring Bean对象。@Indexed/***/}复制代码value: 自定义当前组件或者说bean的名称,可以不配置, 不配置的话默认为组件的首字母小写的类名。该注解只能使用在类,接口、枚举、其他注解上该注解的生命周期是运行时JVM。...
spring @component 详解
lovezhaohaimig的博客
11-08 6174
1、@controller 控制器(注入服务) 2、@service 服务(注入dao) 3、@repository dao(实现dao访问) 4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的) @Component,@Service,@Controller,@Repository注解的类,并把这些类纳入进spring容器中管理。 下面写这个是引入co
spring(十五)----构造方法(第二次后置处理器)
qq_37822914的博客
03-04 250
构造方法的判断 一、没有构造方法 依照前面的代码,我们将其中一个类先不加入构造方法,然后条件断点: package test.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import o...
多继承多实现
qq_29539827的博客
03-09 197
以上是抽象类和接口的多继承多实现例子,在调用的类里面注入的时候,通过注解的形式注入指定name从而决定走的是哪个一个具体的实现类。子实现通过@Component或@Service表明自己唯一的name。第2个实现类serviceB 它还调用了一个接口,这个接口也是多实现的,这个接口的两个实现类里面的业务逻辑也是打印一行日志。第1个实现类serviceA 直接处理业务逻辑(即打印一行日志,当然大家实际项目中肯不是打印一行日志这么简单)可以用来处理,不同地方调用统一的入口却走的是不一样的逻辑。
Spring @Async注解实现异步任务详解
此外,`<context:component-scan>`用于扫描指定的包,以便Spring能够找到并管理带有@Async注解的类。 代码示例中,一个简单的使用场景可能如下: ```java @Service public class MyService { @Async public void...
写文章

热门文章

  • 聊聊二维码 3536
  • 来了,mghio 的微信红包封面! 3231
  • 论基础理论知识的重要性 1886
  • 对象转换工具 MapStruct 介绍 1483
  • 如何实现 Spring 的 @Component 注解 1373

分类专栏

  • 思考 2篇
  • UNIX 1篇
  • Go 1篇
  • 数据库 1篇
  • 网络协议 1篇
  • Java 49篇
  • 科技 1篇
  • 2020圣诞帽 1篇

最新评论

  • Go 并发模型—Goroutine

    CSDN-Ada助手: 恭喜你这篇博客进入【CSDN每天值得看】榜单,全部的排名请看 https://bbs.csdn.net/topics/616568476。

  • 对象转换工具 MapStruct 介绍

    zuijianren: 这个示例还挺全的

  • 如何在亿级数据中判断一个元素是否存在?

    码农BookSea: 写的很好,加油!

  • Spring 的循环依赖问题

    大家一起学编程(python): 大佬就是大佬,666

  • 一文让你彻底搞懂 Spring Cloud 整合 Feign 的原理!

    mghio: 感谢认可!

大家在看

  • 10.解析解方法推导线性回归——不容小觑的线性回归算法
  • Servlet的学习07--状态管理02
  • 代码实践!如何使用CAMEL Agents构建 GraphRAG ?
  • 2024ICPC网赛第二场补题(持续更新...) 385
  • 大模型备案重点步骤详细说明「干货仅供参考」 1

最新文章

  • 互联网中的情绪价值
  • 为什么要去大城市打拼?
  • 每个人都应该掌握一点备份常识
2024年6篇
2023年8篇
2022年8篇
2021年25篇
2020年24篇
2019年13篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家阿坝玻璃钢卡通雕塑实力厂家深圳公园玻璃钢动物雕塑萍乡大型玻璃钢雕塑现代雕塑玻璃钢黑龙江水果玻璃钢雕塑多少钱云岩区玻璃钢雕塑设计公司玻璃钢仿生雕塑常州玻璃钢雕塑哪里卖佛山玻璃钢仿铜人物雕塑批发价格珠海玻璃钢主题雕塑制作厂彭州玻璃钢门头雕塑亳州抽象玻璃钢雕塑哪家便宜炼钢元素玻璃钢景观雕塑浙江户外商场美陈供应商北京公园玻璃钢雕塑联系方式广州黄埔玻璃钢雕塑大同艺术玻璃钢雕塑厂宣威市玻璃钢雕塑设计批发汕头品牌玻璃钢卡通雕塑重庆商场美陈公司安顺玻璃钢景观雕塑泰安校园玻璃钢雕塑湘西佛像玻璃钢雕塑长沙花朵玻璃钢景观雕塑厂家玻璃钢雕塑厂家推荐品牌玻璃钢卡通雕塑销售方法湖北园林景观玻璃钢雕塑艺术小品城市几何玻璃钢雕塑哪家好石家庄定制玻璃钢雕塑制作雕塑玻璃钢造型彩绘大象香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化