手写Spring框架(上)浅出

手写Spring框架

  • 准备工作
  • Spring启动和扫描逻辑实现
  • 依赖注入的实现
  • Aware回调模拟实现和初始化机制模拟实现
  • BeanPostProcessor (Bean的后置处理器) 模拟实现
  • Spring AOP 模拟实现

准备工作

  1. 准备一个空的工程
  2. 创建spring的容器类,它是Spring IOC理念的实现,负责对象的实例化、对象和对象之间依赖关系配置、对象的销毁、对外提供对象的查找等操作,对象的整个生命周期都是由容器来控制。传统使用方法是传入一个spring的配置文件或配置类根据用户的配置来创建这个容器。
package com.spring;public class EditApplicationContext {//传入配置类private Class configClass;public EditApplicationContext(Class configClass) {this.configClass = configClass;}//定义根据别名获取类的方法public Object getBean(String name){return null;}
}
  1. 定义一个配置类,相当于配置文件
package com.zedit;import com.spring.ComponentScan;//指定包扫描路径
@ComponentScan("com.zedit.service")
public class AppConfig {
}
- 如何定义包扫描路径,编写一个注解类
@Retention(RetentionPolicy.RUNTIME)
//规定只能写在类上
@Target(ElementType.TYPE)
public @interface ComponentScan {//接收属性值,指定扫描路径String value() default "";
}
  1. 定义一个Component注解,它的作用就是将类交给spring容器,实现bean的注入
@Retention(RetentionPolicy.RUNTIME)
//规定只能写在类上
@Target(ElementType.TYPE)
public @interface Component {//提供默认值String value() default "";
}

Spring启动和扫描逻辑实现

  1. 传入配置类对于spring而言 它只需要判断配置类有没有它提供的注解,获取扫描路径值,根据路径值
  2. 通过类加载器加载目录下的类,首先获取所有文件,然后获取全限定类名
public EditApplicationContext(Class configClass) {this.configClass = configClass;//解析配置类//Component注解->扫描路径->扫描ComponentScan declaredAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);String path = declaredAnnotation.value();// 全限定类名加工成能用的路径名 "com/xuhua/service"path = path.replace(".", "/");ClassLoader classLoader = EditApplicationContext.class.getClassLoader();//根据AppClassLoader加载器目录获取 classPath目录下中的‘path’目录下的资源URL resource = classLoader.getResource(path);//判断是否是文件夹而不是单个文件if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileName = f.getAbsolutePath();// /Users/zhuxuhua/Desktop/project/spring-edit/target/classes/com/zedit/service/XxxUtils.class// 转换成 com.zedit.service.XxxUtilsif (fileName.endsWith(".class")) {String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));className = className.replace("/", ".");try {//根据全限定类名加载类Class<?> clazz = classLoader.loadClass(className);//判断扫描到的类是不是一个bean注解if (clazz.isAnnotationPresent(Component.class)){}} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}}}
}
  1. 根据@Scope 注解判断bean是单例还是原型
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value();
}
  1. 定义单例池
    在这里插入图片描述
  2. 由于在使用bean和初始化bean时都要去解析bean的定义与他的注解,如果不做设计每次的解析就会显得冗余繁琐,所以spring在Context扫描阶段定义了一个BeanDefinition定义类,它记录了bean的各种信息,先将扫描到的bean填入BeanDefinitionMap随后处理单例对象

//存储单例对象的单例池
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
//存储所有bean的定义
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
-------
try {Class<?> clazz = classLoader.loadClass(className);if (clazz.isAnnotationPresent(Component.class)) {//表示当前这个类有Component注解是一个bean对象//解析类,判断scope注解是单例的bean还是 prototype的bean//每扫描到一个bean就定义一个BeanDefinition对象Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);String beanName = componentAnnotation.value();BeanDefinition beanDefinition = new BeanDefinition();//spring bean默认为多例模式beanDefinition.setScpoe("prototype");if (clazz.isAnnotationPresent(Scope.class)){Scope annotation = clazz.getAnnotation(Scope.class);String value = annotation.value();if (value.equals("singleton")){beanDefinition.setScpoe("singleton");}}beanDefinition.setClazz(clazz);//扫描到的所有bean都存入这个mapbeanDefinitionMap.put(beanName,beanDefinition);}} catch (ClassNotFoundException e) {throw new RuntimeException(e);
}
  1. 扫描完后根据存储的beanDefinitionMap填入单例池
    在这里插入图片描述
  2. 获取bean方法中判断是否是单例bean,如果是直接从单例池中取,如果不是则创建bean
public Object getBean(String beanName){//获取bean 如果map中没有就抛出异常,说明她不是一个bean,没有被扫描到if (beanDefinitionMap.containsKey(beanName)){BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判断scope值,单例直接从单例池中取if (beanDefinition.getScpoe().equals("singleton")){return singletonObjects.get(beanName);}else {//原型bean每次从新创建return createBean(beanDefinition);}}else {throw new NullPointerException();}}//用beanDefinition中的clazz信息通过反射创建bean
public Object createBean(BeanDefinition beanDefinition){Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}
}

依赖注入的实现

首先注解,能标注在成员变量上

Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {}
@Component("userService")
@Scope("singleton")
public class UserService {@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}
}

依赖注解的实现原理就是在启动扫描初始化阶段 spring创建bean时 给@Autowired的成员变量赋值

//用beanDefinition中的clazz信息通过反射创建beanpublic Object createBean(BeanDefinition beanDefinition){Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//依赖注入实现原理for (Field declaredField : clazz.getDeclaredFields()) {if (declaredField.isAnnotationPresent(Autowired.class)){Object bean = getBean(declaredField.getName());declaredField.setAccessible(true);declaredField.set(instance,bean);}}return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}

在这里插入图片描述

Aware回调模拟实现和初始化机制模拟实现

需要回调的实现接口方法,在初始化阶段bean的创建阶段将beanName通过反射设置值

//回调接口
public interface BeanNameAware {void setBeanName(String name);
}-------public interface InitializingBean {void afterPropertySet();
}

@Component("userService")
@Scope("singleton")
public class UserService implements BeanNameAware, InitializingBean {@Autowiredprivate OrderService orderService;private String beanName;@Overridepublic void setBeanName(String name) {beanName = name;}@Overridepublic void afterPropertySet() {System.out.println("初始化");}
//用beanDefinition中的clazz信息通过反射创建beanpublic Object createBean(String beanName,BeanDefinition beanDefinition){Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//依赖注入for (Field declaredField : clazz.getDeclaredFields()) {if (declaredField.isAnnotationPresent(Autowired.class)){Object bean = getBean(declaredField.getName());declaredField.setAccessible(true);declaredField.set(instance,bean);}}//aware 回调if (instance instanceof BeanNameAware){((BeanNameAware) instance).setBeanName(beanName);}//反射调用初始化bean的方法if (instance instanceof InitializingBean){((InitializingBean) instance).afterPropertySet();}return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}

BeanPostProcessor (Bean的后置处理器) 模拟实现

spring的扩展机制,在bean初始化前后调用

//定义接口  有初始化前后两种操作,也可以添加更多
public interface BeanPostProcessor {Object postProcessorBeforeInitialization(Object bean,String beanName);Object postProcessorAfterInitialization(Object bean,String beanName);
}----------------//自定义 BeanPostProcessor 实现BeanPostProcessor接口
@Component
public class ZhuZhuBanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessorBeforeInitialization(Object bean, String beanName) {System.out.println("初始化前");//定制操作if (beanName.equals("userService")) {System.out.println("userService 初始化前");}return null;}@Overridepublic Object postProcessorAfterInitialization(Object bean, String beanName) {System.out.println("初始化后");return null;}
}--------//同其他bean一样在扫描时 加载 判断是否实现了BeanPostProcessor,如果实现了就放入 专门的List存储//scan方法中  判断此类是否实现了BeanPostProcessor,并存入list
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor beanPostProcessorInstance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();beanPostProcessorList.add(beanPostProcessorInstance);
}---------//createBean方法中//createBean 时调  初始化前调用   
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在调用初始化方法前 重新赋值对象instance = beanPostProcessor.postProcessorBeforeInitialization(instance,beanName);
}//初始化
if (instance instanceof InitializingBean){try {((InitializingBean) instance).afterPropertySet();} catch (Exception e) {throw new RuntimeException(e);}
}//createBean 时调  初始化后调用   
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在调用初始化方法后 重新赋值对象instance = beanPostProcessor.postProcessorAfterInitialization(instance, beanName);
}

Spring AOP 模拟实现

使用jdk动态代理 实现

@Component("userService")
@Scope("singleton")
public class UserServiceImpl implements UserService{@Autowiredprivate OrderService orderService;private String beanName;@Overridepublic void setBeanName(String name) {beanName = name;}@Overridepublic void afterPropertySet() {System.out.println("初始化");}@Overridepublic void test(){System.out.println(orderService+"orderService test");System.out.println(beanName);}}--------public interface UserService {void test();
}

结合 BeanPostProcessor 完成jdk动态的实现

    @Overridepublic Object postProcessorAfterInitialization(Object bean, String beanName) {System.out.println("初始化后");if (beanName.equals("userService")){Object proxyInstance = Proxy.newProxyInstance(ZhuZhuBanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("被代理的逻辑");return method.invoke(bean,args);}});return proxyInstance;}return bean;}

被动态代理后的类,执行类中的任意方法 都会经过 jdk的代理逻辑进行增强

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/293808.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Yolo 自制数据集dect训练改进

上一文请看 Yolo自制detect训练-CSDN博客 简介 如下图&#xff1a; 首先看一下每个图的含义 loss loss分为cls_loss, box_loss, obj_loss三部分。 cls_loss用于监督类别分类&#xff0c;计算锚框与对应的标定分类是否正确。 box_loss用于监督检测框的回归&#xff0c;预测框…

算法——距离计算

距离计算常用的算法包括欧氏距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、余弦相似度等。这些算法在数据挖掘、机器学习和模式识别等领域中被广泛应用。 1.欧氏距离 欧式距离也称欧几里得距离&#xff0c;是最常见的距离度量&#xff0c;衡量的是多维空间中两个点之间的…

SpringMvc项目创建过程

1、新建空项目 名字和路径自定义&#xff0c;Maven项目&#xff0c;不建议勾选Add sample code 2、创建web模块 选中当前项目 修改路径&#xff0c;注意是在main包下 选择当前项目 3、编写pom.xml文件 在文件中加入以下内容&#xff0c;packaging标签表明了maven打包类型。 &…

Mysql的高级语句3

目录 一、子查询 注意&#xff1a;子语句可以与主语句所查询的表相同&#xff0c;但是也可以是不同表。 1、select in 1.1 相同表查询 1.2 多表查询 2、not in 取反&#xff0c;就是将子查询结果&#xff0c;进行取反处理 3、insert into in 4、update…

【智能算法】黄金正弦算法(GSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2017年&#xff0c;Tanyildizi等人受到正弦函数单位圆内扫描启发&#xff0c;提出了黄金正弦算法&#xff08;Golden Sine Algorithm, GSA&#xff09;。 2.算法原理 2.1算法思想 GSA来源于正弦函…

阿里云服务器安装SSL证书不起作用的解决方案

阿里云服务器安装SSL证书不起作用的解决方案 在阿里云安装SSL证书后&#xff0c;访问无效&#xff0c;各种检查证书安装没有问题。忽然想到阿里云默认连80端口都没开启&#xff0c;443端口应该也没开启。 登录阿里云控制台 - 云服务器 ECS - 网络与安全 - 安全组 - 管理规则 - …

http模块 服务器端如何响应(获取)静态资源?

一、静态资源与动态资源介绍&#xff1a; &#xff08;1&#xff09;静态资源 内容长时间不改变的资源。eg&#xff1a;图片、视频、css js html文件、字体文件... &#xff08;2&#xff09;动态资源 内容经常更新的资源。eg&#xff1a;百度首页、淘宝搜索列表... 二、服…

栈————顺序栈和链式栈

目录 栈 顺序栈 1、初始化顺序栈 2、判栈空 3、进栈 4、出栈 5、读栈顶元素 6、遍历 链式栈 1、初始化链式栈 2、断链式栈是否为空判 3、入栈(插入) ​编辑​编辑 4、出栈(删除) 5、读取栈顶元素 6、输出链式栈中各个节点的值&#xff08;遍历&#xff09; 栈 …

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-04-01 1…

浅谈iOS开发中的自动引用计数ARC

1.ARC是什么 我们知道&#xff0c;在C语言中&#xff0c;创建对象时必须手动分配和释放适量的内存。然而&#xff0c;在 Swift 中&#xff0c;当不再需要类实例时&#xff0c;ARC 会自动释放这些实例的内存。 Swift 使用 ARC 来跟踪和管理应用程序的内存&#xff0c;其主要是由…

2022 Tesla AI Day -特斯拉自动驾驶FSD的进展和算法软件技术之数据以及虚拟

2022 Tesla AI Day -特斯拉自动驾驶FSD的进展和算法软件技术之数据以及虚拟 附赠自动驾驶学习资料和量产经验&#xff1a;链接 人工智能算法犹如电影的主演&#xff0c;我们很多时候看电影只看到主演们的精彩&#xff0c;但其实电影的创意和呈现都来自于背后的导演和制片等团队…

服务器停止解析域名,但仍然可以访问到

1.centos7 如何刷新dns缓存 在CentOS 7上&#xff0c;DNS缓存由nscd&#xff08;Name Service Cache Daemon&#xff09;管理&#xff0c;如果系统上安装了nscd&#xff0c;可以通过清除nscd缓存来刷新DNS缓存。 要刷新DNS缓存&#xff0c;请执行以下命令&#xff1a; sudo …

下载及安装PHP,composer,phpstudy,thinkPHP6.0框架

文章目录 目录 文章目录 前言 一、下载PHP 二、下载composer 三、下载PHPstudy 四、下载think PHP 1.下载 2.多应用开发 前言 thinkPHP是一款开源的PHP框架&#xff0c;它是基于MVC&#xff08;Model-View-Controller&#xff09;设计模式构建的。thinkPHP提供了丰富的…

多微信聚合聊天神器,让你的社交更高效!

对于那些拥有多个微信号的用户来说&#xff0c;频繁地在不同微信号和设备之间切换既麻烦又容易搞混。这时候&#xff0c;一款多微信聚合聊天神器——微信管理系统应运而生&#xff0c;为我们带来了极大的便利与高效。 下面一起来看看它都有哪些功能吧&#xff01; 1、多微信同…

Unity 学习日记 12.小球撞击冰块游戏

目录 1.准备场景 2.让小球动起来 3.用鼠标把小球甩出去 4.加入鼠标点击小球的判断 5.小球与冰块的碰撞测试 6.撞击后销毁冰块 ​编辑 7.显示游戏计时 8.显示扔球次数 9.显示剩余冰块个数 10.游戏结束 11.完整代码 下载源码 UnityPackage 最终效果&#xff1a; 1.准…

讲解pwngdb的用法,以csapp的bomb lab phase_1为例

参考资料 Guide to Faster, Less Frustrating Debugging 什么情况下会使用gbd 需要逆向ELF文件时(掌握gdb的使用&#xff0c;是二进制安全的基本功)开发程序时&#xff0c;程序执行结果不符合预期 动态调试ELF文件可以使用另外一种方法&#xff1a;IDA的远程linux动态调试。个…

AI在行业大模型中的机会及爆发赚钱的行业有哪些?

人工智能(AI)正逐渐成为驱动各行业发展的核心力量,尤其是在应用层,AI结合具体细分领域所带来的生产力提升是巨大的。随着技术的不断进步和人口老龄化趋势的加剧,AI将在多个行业中发挥关键作用,为这些行业带来爆发式增长和丰厚的利润。 一、医疗行业:AI辅助诊断的崛起…

蓝桥杯刷题_day7_动态规划_路径问题

文章目录 DAY7下降路径最小和最小路径和地下城游戏 DAY7 下降路径最小和 【题目描述】 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从每一行中选择一个元…

FreeRTOS_day2:2024/4/1

1.总结串口的发送和接收功能使用到的函数(见思维导图) 2.总结DMA的作用&#xff0c;和DMA空闲中断的使用方式(见思维导图) 3.使用PWMADC光敏电阻完成光控灯的实验 int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration------------------------…

CentOS7安装DockerCompose

1.CentOS7安装DockerCompose 1.1.下载 Linux下需要通过命令下载&#xff1a; # 安装 curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-uname -s-uname -m > /usr/local/bin/docker-compose1.2.修改文件权限 修改文件权限&#xff1a…