Spring-手写模拟Spring底层原理
概述
模拟大致的底层原理,为学习Spring源码做铺垫。
实现的功能:扫描路径、依赖注入、aware回调、初始化前、初始化、初始化后、切面
未实现的功能:构造器推断、循环依赖
重点:BeanDefinition、BeanPostProcessor
学习Spring源码的重点:设计模式、编码规范、设计思想、扩展点
启动类:
public class Yeah
{public static void main(String[] args){GaxApplicationContext gaxApplicationContext = new GaxApplicationContext(AppConfig.class);UserInterface userService = (UserInterface) gaxApplicationContext.getBean("userService");userService.test();}
}
关键方法:
public class GaxApplicationContext
{private Class configClass;private static final String SINGLETON = "singleton";private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();private Map<String, Object> singletonObjects = new HashMap<>();// Spring源码用的是LinkedListprivate List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public GaxApplicationContext(Class configClass){this.configClass = configClass;// 扫描指定路径,找到所有@Component注解的类,封装成beanDefinition保存再Map中scan(configClass);// 思考个问题:beanDefinitionMap.keySet()和beanDefinitionMap.entrySet()两种遍历的区别?选用哪个好?for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()){String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();if (SINGLETON.equals(beanDefinition.getScope())){Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);}}}private Object createBean(String beanName, BeanDefinition beanDefinition){Class clazz = beanDefinition.getType();Object instance = null;try{// 直接使用默认的无参构造器,多个构造器的场景未实现instance = clazz.getConstructor().newInstance();// 属性填充,依赖注入for (Field field : clazz.getDeclaredFields()){if (field.isAnnotationPresent(Autowired.class)){field.setAccessible(true);field.set(instance, getBean(field.getName()));}}// aware回调if (instance instanceof BeanNameAware){((BeanNameAware)instance).setBeanName(beanName);}// 初始化前for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}// 初始化if (instance instanceof InitializingBean){((InitializingBean)instance).afterPropertiesSet();}// 初始化后for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);}}catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e){e.printStackTrace();}return instance;}public Object getBean(String beanName){if (!beanDefinitionMap.containsKey(beanName)){throw new NullPointerException();}BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (SINGLETON.equals(beanDefinition.getScope())){Object singletonBean = singletonObjects.get(beanName);if (null == singletonBean){singletonBean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, singletonBean);}return singletonBean;}else{// 原型BeanObject prototypeBean = createBean(beanName, beanDefinition);return prototypeBean;}}private void scan(Class configClass){if (configClass.isAnnotationPresent(ComponentScan.class)){ComponentScan componentScanAnnotation = (ComponentScan)configClass.getAnnotation(ComponentScan.class);String path = componentScanAnnotation.value();// path = com/gax/servicepath = path.replace(".", "/");ClassLoader classLoader = GaxApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);assert resource != null;File file = new File(resource.getFile());if (file.isDirectory()){for (File f : Objects.requireNonNull(file.listFiles())){String absolutePath = f.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));// 类加载器入参需要的格式:com.gax.service.XXXabsolutePath = absolutePath.replace("\\", ".");try{Class<?> clazz = classLoader.loadClass(absolutePath);if (clazz.isAnnotationPresent(Component.class)){if (BeanPostProcessor.class.isAssignableFrom(clazz)){BeanPostProcessor instance = (BeanPostProcessor)clazz.getConstructor().newInstance();beanPostProcessorList.add(instance);}Component componentAnnotation = clazz.getAnnotation(Component.class);String beanName = componentAnnotation.value();if ("".equals(beanName)){// 默认beanNamebeanName = Introspector.decapitalize(clazz.getSimpleName());}BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(clazz);if (clazz.isAnnotationPresent(Scope.class)){Scope scopeAnnotation = clazz.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);}else{// 默认单例BeanbeanDefinition.setScope(SINGLETON);}beanDefinitionMap.put(beanName, beanDefinition);}}catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException| InstantiationException | IllegalAccessException e){e.printStackTrace();}}}}}
}
gitee地址:
git clone https://gitee.com/seek6174/spring-seek.git
相关文章:
Spring-手写模拟Spring底层原理
概述 模拟大致的底层原理,为学习Spring源码做铺垫。 实现的功能:扫描路径、依赖注入、aware回调、初始化前、初始化、初始化后、切面 未实现的功能:构造器推断、循环依赖 重点:BeanDefinition、BeanPostProcessor 学习Spring…...
Scala【集合常用方法和函数操作(下)】
Fold、FoldLeft 和 FoldRight object Test03_Fold {def main(args: Array[String]): Unit {// 称作集合外的参数val list List(1,2,3,4)// fold的底层仍然是调用的 foldLeft// 第一个参数是一个值(称作集合内的参数,必须和集合外的参数类型一致)// 第二个参数是一…...
JS加密/解密之那些不为人知的基础逻辑运算符
不多说,直接上干货 使用逻辑非运算符 ! 和双重逻辑非运算符 !!:例如 ![]、!![]、!0、!!0 和 !""、!!""。空字符串的转换:!"" 和 !!""。数组和对象的类型转换:[] []、[] - []、{} [] 和…...
flinksql kafka到mysql累计指标练习
flinksql 累计指标练习 数据流向:kafka ->kafka ->mysql 模拟写数据到kafka topic:wxt中 import com.alibaba.fastjson.JSONObject; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Produ…...
pdf转jpg的方法【ps和工具方法】
pdf转jpg的方法: 1.photoshop办法: pdf直接拖入ps中,另存为*.Jpg文件即可 另外注意的时候,有时候别人给你pdf文件中包含你需要的jpg文件,千万不要截图进入ps中,直接把文件拖入ps中,这样的文件…...
【已解决】Qt发送信号后,槽函数没有响应
Qt发送信号后,槽函数没有响应 检查有没有连接正确的信号和槽函数,有时候,大意了,会写错检查connect函数返回值,有没有连接成功检查对象的创建方式,确保在信号发送前,以及槽函数接收前ÿ…...
Kafka入门05——基础知识
目录 副本数据同步原理 HW和LEO的更新流程 第一种情况 第二种情况 数据丢失的情况 解决方案 Leader副本的选举过程 日志清除策略和压缩策略 日志清除策略 日志压缩策略 Kafka存储手段 零拷贝(Zero-Copy) 页缓存(Page Cache&…...
WordPress(7)配置邮箱发送功能
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、注册登陆163邮箱1. 配置SMTP二、开启smtp1.添加授权码二.在网站中配置smtp服务1.在主题的Boxmoe主题设置中开启邮箱设置三.安装所需要的插件1.安装完毕开启插件即可四.SMTP邮箱服务测试总结…...
简化路径(C++解法)
题目 给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 / 开头),请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身࿱…...
CS224W1.1——图机器学习介绍
文章目录 1. 介绍2. 主要问题3. 深度学习如何应用在图结构中4. 课程大纲 学习一下斯坦福CS224W的图机器学习(2021年),并做一下学习笔记,主要是研究方向与图神经网络相关。这次是第一次笔记,图片很多都是从斯坦福的PPT里…...
docker搭建waline评论系统
我这里是给博客网站嵌入评论系统的 1.登录LeanCloud 国际版,没有账号可以注册个 链接:点击跳转 2.新建应用,选择开发版(免费),商用版每个月最低消费5美刀。 3.在设置-应用凭证里面将AppID、AppKey、Maste…...
sql server 生成连续日期和数字
在sqlserver里,可以利用系统表master..spt_values里面存储的连续数字0到2047,结合dateadd()函数生成连续的日期 select convert (varchar(10),dateadd(d, number, getdate()),23) as workday from master..spt_values where type…...
太极v14.0.4 免ROOT用Xposed
一个帮助你免 Root、免解锁免刷机使用 Xposed 模块的 APP 框架。 模块通过它改变系统和应用的行为,既能以传统的 Root/ 刷机方式运作, 也能免 Root/ 免刷机运行;并且它支持 Android 5.0 ~ 11。 简单来说,太极就是个 Xposed 框架…...
python DevOps
在云原生中,python扮演的角色是什么? 在云原生环境中,Python 作为一种高级编程语言,在多个方面扮演着重要角色。云原生是指利用云计算的各种优势(如弹性、可扩展性和自动化),构建和运行应用程序…...
Git(四)底层命令:git对象、树对象、提交对象
目录 一、知识回顾1.1 Linux 基础命令1.2 .git 文件夹解析 二、git 对象(数据对象)2.1 hash-object 存储对象2.2 cat-file 查看对象 三、树对象3.1 ls-files 查看暂存区3.2 update-index 创建暂存区3.3 write-tree 生成树对象3.4 更新暂存区,…...
LVS-DR模式+keepalived+nginx+tomcat实现动静分离、负载均衡、高可用实验
实验条件: test2——20.0.0.20——主服务器——ipvsadm、keepalived服务 test3——20.0.0.30——备服务器——ipvsadm、keepalived服务 nginx5——20.0.0.51——后端真实服务器1(tomcat的代理服务器)——nginx服务 nginx6——20.0.0.61—…...
canvas 状态管理
本文简介 带尬猴,我是德育处主任 canvas 绘图时会根据当前状态来绘制。很多的 canvas 库都利用到这一特性。比如 p5.js 利用了 canvas 状态特性衍生出 push 和 pop 函数实现状态隔离(既然提到了,下一篇就讲这个)。 有兴趣了解 p…...
vue中如何给后端过来的数组中每一个对象加一个新的属性和新的对象(不影响后端的原始数据)
方法: 先看后端的原数据 1、给数组中每一个对象加一个新的属性: 输出查看数组list的值: 2、给数组list加入新的对象: 输出结果: 3、总结: 如果是数组中每个对象新增属性就用map遍历每个对象加入新增的属性…...
SpringAOP源码解析之TargetSource(四)
前言 在Spring框架中,TargetSource是一个接口,用于封装获取目标对象(也就是被代理的对象)的逻辑。它的主要作用是提供代理对象使用的目标对象,并且允许在运行时动态地切换目标对象。TargetSource在Spring的AOP&#x…...
Centos7 安装nvidia显卡驱动
参考一:https://blog.csdn.net/awen19921106/article/details/131331450 参考二:https://www.cnblogs.com/lishanyang/p/17326021.html 报错一: ERROR: Unable to find the kernel source tree for the currently running kernel. Please …...
如何彻底解决Android Studio中文界面兼容性问题:专业级终极配置指南
如何彻底解决Android Studio中文界面兼容性问题:专业级终极配置指南 【免费下载链接】AndroidStudioChineseLanguagePack AndroidStudio中文插件(官方修改版本) 项目地址: https://gitcode.com/gh_mirrors/an/AndroidStudioChineseLanguagePack 还…...
3分钟掌握抖音智能批量下载:全流程自动化解决方案
3分钟掌握抖音智能批量下载:全流程自动化解决方案 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. …...
如何用 Kinovea 实现专业运动分析?免费视频解析工具完全指南
如何用 Kinovea 实现专业运动分析?免费视频解析工具完全指南 【免费下载链接】Kinovea Video solution for sport analysis. Capture, inspect, compare, annotate and measure technical performances. 项目地址: https://gitcode.com/gh_mirrors/ki/Kinovea …...
终极Splide轮播组件路线图:从4.1.4到未来版本的升级指南与特性前瞻
终极Splide轮播组件路线图:从4.1.4到未来版本的升级指南与特性前瞻 【免费下载链接】splide Splide is a lightweight, flexible and accessible slider/carousel written in TypeScript. No dependencies, no Lighthouse errors. 项目地址: https://gitcode.com/…...
Qwen3-TTS效果实测:10种语言语音合成,声音自然度惊艳展示
Qwen3-TTS效果实测:10种语言语音合成,声音自然度惊艳展示 1. 引言:语音合成的新标杆 今天我要带大家体验一款让我眼前一亮的语音合成模型——Qwen3-TTS。这个模型最吸引我的地方是它支持10种语言的语音合成,而且通过简单的自然语…...
抽象类抽象方法
抽象类与抽象方法的核心关系抽象类中可以包含0到多个抽象方法,抽象方法必须存在于抽象类中。抽象类的作用是为子类提供通用模板,强制子类实现特定行为规范,避免设计随意性。public abstract class Person {// 具体方法:子类可直接…...
解决GLIBC版本冲突:手把手编译低版本libcrypto.so.1.0.0(附完整脚本)
解决GLIBC版本冲突:手把手编译低版本libcrypto.so.1.0.0(附完整脚本) 在嵌入式开发中,经常会遇到目标设备的GLIBC版本过低,而编译环境中的库文件版本过高导致的兼容性问题。这种问题通常表现为运行时出现类似version G…...
TEST文件夹:Pytest,集成测试,单元测试
在复杂的自动驾驶项目中,哪怕你只改了一行代码,都可能导致整个感知或控制系统崩溃。如果直接去训练,还会消耗大量算力。所以当你新写了一个功能(比如你改了采样逻辑),先不要急着去跑训练。先跑一下测试&…...
35岁程序员收藏!转行大模型,抢占高薪风口,从入门到高薪 Offer 全攻略
35岁程序员收藏!转行大模型,抢占高薪风口,从入门到高薪 Offer 全攻略 35岁程序员面临职业瓶颈,大模型技术提供了转行出路。文章分析了为何转行大模型是明智之选,包括行业风口、需求缺口大、原有技术可复用、职业生命周…...
Go语言的项目结构:从单体到微服务
Go语言的项目结构:从单体到微服务 项目结构的重要性 在软件开发中,项目结构是影响代码质量和可维护性的关键因素。一个良好的项目结构可以: 提高代码的可读性和可维护性促进团队协作和代码共享便于测试和部署支持代码的重用和扩展降低项目…...
