Spring源码注解篇二:手写@Component注解
@Component注解的功能
在Spring框架中,@Component
注解是一个核心特性,用于自动检测类并将其注册为Spring应用上下文中的Bean。这大大简化了Bean的配置过程,使得开发者能够通过注解的方式快速地将类标记为组件,并由Spring容器进行管理。本文将首先探讨@Component
注解的定义,然后设计并实现一个自定义的@MyselfComponent
注解,该注解将具备与@Component
相同的功能,即自动创建并管理对应的实例。
一、Spring中@Component注解的定义
在Spring的源码中,@Component
注解本身并没有直接实现Bean的创建和管理逻辑,它主要是一个标记注解,用于被Spring的组件扫描机制识别。@Component
注解通常与@ComponentScan
注解一起使用,后者指示Spring框架在哪些包中查找带有@Component
(或它的衍生注解如@Service
、@Repository
、@Controller
)的类,并将这些类实例化为Bean。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {String value() default "";
}
从这个定义可以看出,@Component
注解非常简单,主要标记了注解的目标类型(TYPE
,即类、接口或枚举类型),并定义了注解的保留策略(RUNTIME
),以便在运行时通过反射访问注解信息。
二、设计自定义@MyselfComponent注解
为了设计一个与@Component
功能相似的自定义注解@MyselfComponent
,我们需要考虑以下几点:
- 注解定义:定义
@MyselfComponent
注解,使其能够标记类。 - 组件扫描:实现或集成一个组件扫描机制,用于识别并处理带有
@MyselfComponent
注解的类。 - Bean注册:将扫描到的类实例化为Bean,并注册到Spring应用上下文中。
注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyselfComponent {String value() default "";
}
组件扫描与Bean注册
由于Spring框架提供了丰富的API来处理组件扫描和Bean注册,我们可以利用这些API来简化我们的实现。但为了演示目的,我们将手动模拟这一过程。
三、动手实现@MyselfComponent注解的功能
在实际项目中,我们通常不会完全从头开始实现Spring的组件扫描和Bean注册逻辑,因为这需要深入了解Spring的底层架构。但在这里,我们将通过创建一个简单的模拟框架来展示这一过程。
步骤 1: 创建模拟的ApplicationContext
import java.util.HashMap;
import java.util.Map;public class SimpleApplicationContext {private Map<String, Object> beans = new HashMap<>();public void registerBean(String name, Object bean) {beans.put(name, bean);}public <T> T getBean(String name, Class<T> requiredType) {return (T) beans.get(name);}// 模拟扫描并注册Bean的逻辑...
}
步骤 2: 实现扫描逻辑
这里我们仅模拟扫描过程,不深入Java反射和类路径扫描的具体实现。
public class SimpleComponentScanner {private SimpleApplicationContext context;public SimpleComponentScanner(SimpleApplicationContext context) {this.context = context;}public void scan(String packageName) {// 这里应该使用反射和类加载器来查找指定包下的类,并检查是否带有@MyselfComponent注解// 但为了简化,我们直接模拟注册context.registerBean("myService", new MyService()); // 假设MyService类上有@MyselfComponent注解}
}
步骤 3: 使用
@MyselfComponent
public class MyService {// 类的实现...
}public class Main {public static void main(String[] args) {SimpleApplicationContext context = new SimpleApplicationContext();SimpleComponentScanner scanner = new SimpleComponentScanner(context);scanner.scan("org.springframework.annotation"); // 假设MyService位于这个包下MyService myService = context.getBean("myService", MyService.class);// 使用myService...}
}
结论
虽然上述实现非常基础且远未达到Spring框架的复杂性和功能全面性,但它为理解@Component
注解的工作原理以及如何在自定义框架中模拟类似功能提供了一个基本的框架。在实际应用中,Spring的组件扫描和Bean注册过程要复杂得多,它涉及到多个组件的协同工作,包括ClassPathScanningCandidateComponentProvider
、BeanDefinitionRegistry
、AnnotationBeanNameGenerator
等。
深化理解
为了更深入地理解并模拟Spring的组件扫描和Bean注册过程,我们可以考虑以下几个关键点:
-
使用Java反射API:
- 遍历指定包下的所有类。
- 检查每个类上是否存在
@MyselfComponent
注解。 - 如果存在,则创建该类的实例,并生成一个合适的Bean名称(通常基于类名)。
-
注册Bean到应用上下文:
- 在Spring中,这通常涉及到操作
BeanDefinitionRegistry
,但在我们的模拟框架中,我们可能只是简单地将Bean实例存储在一个Map中。 - 需要确保Bean的注册过程支持依赖注入、生命周期管理等高级功能(尽管在我们的简化示例中未实现这些)。
- 在Spring中,这通常涉及到操作
-
处理注解的属性:
@MyselfComponent
注解中的value
属性可以用来指定Bean的名称。如果没有指定,可以生成一个默认名称(如类名的首字母小写形式)。
-
集成Spring的现有机制(可选):
- 如果可能,可以尝试集成Spring的
ClassPathScanningCandidateComponentProvider
等类来简化扫描过程。 - 使用Spring的
AnnotationConfigApplicationContext
或AnnotationBeanNameGenerator
等类来更紧密地模拟Spring的行为。
- 如果可能,可以尝试集成Spring的
示例扩展
以下是一个更接近于实际Spring行为的SimpleComponentScanner
实现示例,尽管它仍然使用了简化的Bean注册方式:
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;import java.lang.reflect.Modifier;
import java.util.Set;// 假设我们使用Spring的ClassPathScanningCandidateComponentProvider来扫描类
// 但为了简化,这里只描述如何调用它,并不实现完整的类
public class AdvancedComponentScanner {private SimpleApplicationContext context;public AdvancedComponentScanner(SimpleApplicationContext context) {this.context = context;}public void scan(String basePackage) {// 这里应该使用ClassPathScanningCandidateComponentProvider来扫描// 但为了简化,我们直接模拟扫描到的类Class<?> clazz = MyService.class; // 假设MyService是我们扫描到的类if (clazz.isAnnotationPresent(MyselfComponent.class) && !Modifier.isAbstract(clazz.getModifiers())) {String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);context.registerBean(beanName, clazz.getDeclaredConstructor().newInstance());}// 注意:这里省略了异常处理、依赖注入等复杂逻辑}
}
注意:上述代码中的clazz.getDeclaredConstructor().newInstance()
是不推荐的做法,因为它不处理任何可能的异常(如NoSuchMethodException
、IllegalAccessException
、InstantiationException
、InvocationTargetException
),并且从Java 9开始,newInstance()
方法已被标记为过时。在实际应用中,应该使用Constructor.newInstance(Object... initargs)
的替代方法,如通过Class.getDeclaredConstructor().newInstance(initargs)
(需要处理异常)或使用更现代的反射API,如MethodHandles.Lookup
。
此外,上面的代码也没有处理类加载器的问题,这在处理复杂的应用程序和类路径时非常重要。在Spring中,这些都被优雅地处理了。
总之,模拟Spring的@Component
注解功能是一个很好的学习练习,它可以帮助我们更深入地理解Spring的IoC容器和组件扫描机制。然而,要完全实现Spring的功能,需要深入了解Spring的源码和Java的反射API。
相关文章:

Spring源码注解篇二:手写@Component注解
Component注解的功能 在Spring框架中,Component 注解是一个核心特性,用于自动检测类并将其注册为Spring应用上下文中的Bean。这大大简化了Bean的配置过程,使得开发者能够通过注解的方式快速地将类标记为组件,并由Spring容器进行管…...

云备份服务端
文件使用工具和json序列化反序列化工具 //文件和json工具类的设计实现 #ifndef __UTIL__ #define __UTIL__ #include<iostream> #include<fstream> #include<string> #include <vector> #include<sys/stat.h> #include"bundle.h" #inc…...

Jupyter Notebook 使用教程
Jupyter Notebook 使用教程 目录 概述启动Jupyter Notebook创建新的NotebookNotebook界面介绍使用代码单元格使用Markdown单元格Notebook的基本操作保存和导出Notebook扩展功能和技巧 1. 概述 Jupyter Notebook是一个开源的Web应用程序,允许您创建和共享包含代码…...

Leetcode 100361100367.切割蛋糕的最小总开销
Medium:动态规划搜索(实际就是优化后的dfs) class Solution { public: int f[25][25][25][25] {0};int dp(int row1, int col1, int row2, int col2, vector<int>& horizontalCut, vector<int>& verticalCut){if(row1 …...

单网口设备的IP地址识别-还原-自组网
1.如果知道该设备所在网段: 此时可以使用nmap工具,进行网段扫描: nmap -sn 192.168.0.0/24 256个地址的子网10秒就能扫描一轮。关掉设备,打开设备,diff,基本就可以定位所要找到目标设备的IP 2.如果不知道…...

太速科技-FMC207-基于FMC 两路QSFP+光纤收发子卡
FMC207-基于FMC 两路QSFP光纤收发子卡 一、板卡概述 本卡是一个FPGA夹层卡(FMC)模块,可提供高达2个QSFP / QSFP 模块接口,直接插入千兆位级收发器(MGT)的赛灵思FPGA。支持利用Spartan-6、Virtex-6、Kin…...

昇思25天学习打卡营第13天|munger85
文本解码原理–以MindNLP为例 重要的就是怎么样把数字最后转化成真正的文字。而且自回归模型它会一个字给一个字的预测,下一个字应该是什么? 如果这个模型下载很慢,你就可以通过这种方式从摩大社区进行下载。 这种方式, 每一次候…...

Python - Word转TXT文本,或TXT文本转Word
Word文档(.doc或.docx)和纯文本文件(.txt)是两种常用的文件格式。Word文档通常用于复杂的文档处理和排版,而纯文本文件则用于存储和传输纯文本信息。了解如何在这两种格式之间进行转换能提高工作效率,并便于…...

链接追踪系列-00.es设置日志保存7天-番外篇
索引生命周期策略 ELK日志我们一般都是按天存储,例如索引名为"zipkin-span-2023-03-24",因为日志量所占的存储是非常大的,我们不能一直保存,而是要定期清理旧的,这里就以保留7天日志为例。 自动清理7天以前…...

Vant Ui 最新访问地址
Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 顺带一个顶部导航栏正常写法 先使用吸顶为0,然后再写nav-bar <van-sticky :offset-top"0"> <van-nav-bar class"top-title" title"村集体土地公示&q…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(八)-通过无人机进行无线接入
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...

PTrade量化交易终端常见问题11
盈亏分析为空。 回测详情内,盈亏分析内为空。 1、回测正常结束,并且产生多笔交易; 2、盈亏分析热力图无任何内容,检查支持版本,盈亏分析是在需求单号:202211114089,于PTrade1.0-QTV202301.01.…...

被动的机器人非线性MPC控制
MPC是一种基于数学模型的控制策略,它通过预测系统在未来一段时间内的行为,并求解优化问题来确定当前的控制输入,以实现期望的控制目标。对于非线性系统,MPC可以采用非线性模型进行预测和优化,这种方法被称为非线性模型…...

什么样的服务器是合乎直销网站标准
现在社会的发展,有着越来越多的人想要利用互联网来做直销。做好直销行业系统解决方案离不开好的服务器支持,服务器的的稳定性和速度是直接影响网站后期运作,可以看做是网站的根基。 做网站直销选择租用服务器需要注意的几点要素 一些大的直销互联网公司如安利、雅芳、康宝莱、玫…...

python 语法学习 day13
一.判断题错题反思 1.创建对象是通过调用构造方法完成的 3.python方法定义的第一个参数是self 4.一个对象只能有一个实例变量(错) 5.在python类中,构造方法的名称为__init__ 6.从类定义之外直接访问实例变量是不好的程序设计风格 7.在python中定义类是时…...

Spring MVC中Restful风格引入
一,RESTful概述 在现代Web应用开发中,RESTful架构风格已成为一种标准实践,特别是在构建可扩展的Web服务时。Spring MVC提供了全面的支持来构建遵循REST原则的Web服务。我在此介绍如何在Spring MVC中实现RESTful风格的Web服务,并通…...

C# Winform 系统方案目录的管理开发
在做一个中等复杂程度项目时,我们通常有系统全局配置,还要有对应的方案目录的管理和更新。 比如我们有如下需求:开发一个方案管理,可以新建、打开和保存方案,同时还需要保存方案中的各种文件。我设计的采用目录管理和…...

算法-二叉树常见问题详解
文章目录 1. 二叉树的三种遍历方式的实质2. 二叉树的序列化与反序列化3. 根据前序中序反序列创建二叉树4. 二叉树的路径问题5. LCA公共祖先问题6. 二叉搜索树的LCA问题7. 验证搜索二叉树8. 修建搜索二叉树9. 二叉树打家劫舍问题 1. 二叉树的三种遍历方式的实质 这个相信大家都不…...

【流媒体】 通过ffmpeg硬解码拉流RTSP并播放
简介 目前RTSP拉流是网络摄像头获取图片数据常用的方法,但通过CPU软解码的方式不仅延时高且十分占用资源,本文提供了一种从网络摄像头RTSP硬解码的拉流的方法,并且提供python代码以便从网络摄像头获取图片进行后续算法处理。 下载ffmpeg F…...

Go语言指针及不支持语法汇总
本文为Go语言中指针定义和示例及不支持语法汇总。 目录 指针 定义指针 关键字new定义 函数返回指针 空指针 Go不支持语法汇总 总结 指针 Go语言也有指针,结构体成员调用时,obj.name Go语言在使用指针时,会使用内容的垃圾回收机制&am…...

Why can‘t I access GPT-4 models via API, although GPT-3.5 models work?
题意:为什么我无法通过API访问GPT-4模型,尽管GPT-3.5模型可以工作? 问题背景: Im able to use the gpt-3.5-turbo-0301 model to access the ChatGPT API, but not any of the gpt-4 models. Here is the code I am using to tes…...

MATLAB中Simulink.SimulationData.Dataset用法
目录 语法 说明 示例 访问使用Dataset格式记录的数据 打开模型vdp 使用 Dataset 对象来组合模拟输入信号 Simulink.SimulationData.Dataset的功能是访问已记录的模拟数据或组合模拟输入数据。 语法 ds Simulink.SimulationData.Dataset ds Simulink.SimulationData.Da…...

Spring Security学习笔记(一)Spring Security架构原理
前言:本系列博客基于Spring Boot 2.6.x依赖的Spring Security5.6.x版本 Spring Security中文文档:https://springdoc.cn/spring-security/index.html 一、什么是Spring Security Spring Security是一个安全控制相关的java框架,它提供了一套全…...

nginx的access.log日志输出请求数
适用格式 #log_format main $remote_addr - $remote_user [$time_local] "$request" # $status $body_bytes_sent "$http_referer" # "$http_user_agent" "$http_x_forwarded_for"; 形如: 12…...

前端网站(三)-- 记事本【附源码】
开篇(请大家看完):此网站写给挚爱,后续页面还会慢慢更新,大家敬请期待~ ~ ~ 此前端框架,主要侧重于前端页面的视觉效果和交互体验。通过运用各种前端技术和创意,精心打造了一系列引人入胜的页面…...

java——Junit单元测试
测试分类 黑盒测试:不输入代码,给输入值,看程序能够给出期望的值。 白盒测试:写代码,关注程序具体执行流程。 JUnit单元测试 一个测试框架,供java开发人员编写单元测试。 是程序员测试,即白…...

Scala学习笔记17: Try与异常处理
目录 第十七章 Try与异常处理1- 异常的抛出和捕获1. 异常抛出2. 异常捕获 2- 函数式的错误处理1. Try 类型2. 使用 Try3. 处理 Try 结果4. Try 的常用方法5. Try 的优势总结 end 第十七章 Try与异常处理 1- 异常的抛出和捕获 Scala 的异常处理机制与 Java非常相似, 但也有一些…...

内网信息收集——MSF信息收集浏览器记录配置文件敏感信息
文章目录 一、配置文件敏感信息收集二、浏览器密码&记录三、MSF信息收集 域控:windows server 2008 域内机器:win7 攻击机:kali 就是红日靶场(一)的虚拟机。 一、配置文件敏感信息收集 使用searchall64.exe&#…...

C++ STL中的std::remove_if 的用法详解
在现代C++编程中,标准模板库(STL)提供了一系列功能强大的算法,这些算法极大地简化了日常的编程任务。其中,std::remove_if是一个非常实用的函数,它允许我们从容器中移除满足特定条件的所有元素。本文将深入探讨std::remove_if的使用方法,并通过一个具体的例子——基于St…...

基于AT89C51单片机的16×16点阵LED显示器字符滚动显示设计(含文档、源码与proteus仿真,以及系统详细介绍)
本篇文章论述的是基于AT89C51单片机的1616点阵LED显示器字符滚动显示设计的详情介绍,如果对您有帮助的话,还请关注一下哦,如果有资源方面的需要可以联系我。 目录 仿真效果图 仿真图 代码 系统论文 资源下载 设计的内容和要求 熟悉51系…...