【Spring】我抄袭了Spring,手写一套MySpring框架。。。
这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能
文章目录
- 这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能
- 🚀@ComponentScan注解
- ✈️@Component注解
- 🚁在spring中ioc容器的类是ApplicationContext
- 🚂测试类
- 🚊MySpringConfig类,统一的配置类
- 🚞OrderService类,一个普通的bean
- 🚲来测试一下功能
- 🚡@Scope注解
- 🚟BeanDefinition 类
- 🚠要实现我们的@scope注解,我们需要改造一下我们的代码
- 🚜然后我们从beanDefinitionMap中实例化bean
- 🚙我们来看一下效果
- 🚘@Autowired注解
- 🚗UserService类,orderservice注入该类
- 🚗要使@Autowired注解生效,将bean实例化到一级缓存中方法需要改造一下
- 🚕BeanPostProcessor接口
- 🚖添加自己的BeanPostProcessor
- 🚛Aop
🚀@ComponentScan注解
ComponentScan做的事情就是告诉Spring从哪里找到bean
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {String[] value() default {};}
✈️@Component注解
@Component是spring中的一个注解,它的作用就是实现bean的注入
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default "";}
🚁在spring中ioc容器的类是ApplicationContext
所以我们需要创建一个ApplicationContext,有参构造传入config的class
public class ApplicationContext {public ApplicationContext(Class configClass) {}}
存放bean的map
public class ApplicationContext {private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);public ApplicationContext(Class configClass) {}}
拿到ComponentScan的值
public class ApplicationContext {private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);public ApplicationContext(Class configClass) {//查看是否有ComponentScan注解if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);String[] paths = componentScanAnnotation.value();}}
}
拿到该路径下所有的class文件
public class ApplicationContext {private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);public ApplicationContext(Class configClass) {//查看是否有ComponentScan注解if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);String[] paths = componentScanAnnotation.value();for (String path : paths) {//拿到包路径ClassLoader classLoader = ApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path.replace(".", "/"));//拿到该路径下所有的class文件File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {}}}}}
}
注册有Component注解的bean
public class ApplicationContext {private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);public ApplicationContext(Class configClass) {//查看是否有ComponentScan注解if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);String[] paths = componentScanAnnotation.value();for (String path : paths) {//拿到包路径ClassLoader classLoader = ApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path.replace(".", "/"));//拿到该路径下所有的class文件File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {try {String filePath = f.getPath();//拿到com.masiyi.service.MySpringConfigString sub = filePath.substring(filePath.indexOf("com"), filePath.indexOf(".class"));String classes = sub.replace("\\", ".");Class<?> aClass = classLoader.loadClass(classes);//注册有Component注解的beanif (aClass.isAnnotationPresent(Component.class)) {Object bean = aClass.getDeclaredConstructor().newInstance();Component component = aClass.getAnnotation(Component.class);String beanName = component.value();if ("".equals(beanName) || beanName == null) {singletonObjects.put(f.getName().split("\\.")[0], bean);} else {singletonObjects.put(beanName, bean);}}} catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {e.printStackTrace();}}}}}}
}
🚂测试类
现在我们最基础的spring的ioc已经基本实现了,我们新建一个测试类来测试
public class Test {public static void main(String[] args) {ApplicationContext applicationContext = new ApplicationContext(MySpringConfig.class);}
}
🚊MySpringConfig类,统一的配置类
@ComponentScan("com.masiyi.service")
public class MySpringConfig {}
🚞OrderService类,一个普通的bean
@Component
public class OrderService {
}
🚲来测试一下功能
拿到包路径
拿到该路径下所有的class文件
注册有Component注解的bean
注册进ioc
getBean方法
public Object getBean(String beanName) {return this.singletonObjects.get(beanName);}
🚡@Scope注解
@Scope注解是 Spring IOC 容器中的一个作用域
🚟BeanDefinition 类
BeanDefinition 是定义 Bean 的配置元信息接口,可以理解为创建bean过程中的一个中间类,扩展bean,存储更多的信息
public class BeanDefinition {private String scope;private Class aClass;public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public Class getaClass() {return aClass;}public void setaClass(Class aClass) {this.aClass = aClass;}
}
🚠要实现我们的@scope注解,我们需要改造一下我们的代码
把createBean方法抽离出来
private void createBean(String beanName,Class<?> aClass){//注册有Component注解的beanif (aClass.isAnnotationPresent(Component.class)) {Component component = aClass.getAnnotation(Component.class);BeanDefinition beanDefinition = new BeanDefinition();if (aClass.isAnnotationPresent(Scope.class)) {Scope scope = aClass.getAnnotation(Scope.class);beanDefinition.setScope(scope.value());} else {beanDefinition.setScope("singleton");}beanDefinition.setaClass(aClass);String value = component.value();if ("".equals(value)) {beanDefinitionMap.put(beanName, beanDefinition);} else {beanDefinitionMap.put(value, beanDefinition);}}}
扫描ComponentScan注解的方法体改一下
🚜然后我们从beanDefinitionMap中实例化bean
//将bean实例化到一级缓存中for (String beanName : beanDefinitionMap.keySet()) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//单例if ("singleton".equals(beanDefinition.getScope())) {try {Object bean = beanDefinition.getaClass().getDeclaredConstructor().newInstance();singletonObjects.put(beanName, bean);} catch (Exception e) {e.printStackTrace();}}}
getBean方法也需要改造
public Object getBean(String beanName) {BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new NullPointerException();}if ("singleton".equals(beanDefinition.getScope())) {//如果是单例,直接返回缓存里的beanreturn this.singletonObjects.get(beanName);} else {try {//如果是多例,直接返回新的beanreturn beanDefinition.getaClass().getDeclaredConstructor().newInstance();} catch (Exception e) {e.printStackTrace();}}return null;}
🚙我们来看一下效果
这是没有加scope注解
返回的bean都是一个对象
我们给bean加上scope注解
返回来的bean每个都不一样
🚘@Autowired注解
spring中实现依赖注入的注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
🚗UserService类,orderservice注入该类
@Component("userService")
public class UserService {
}
@Component
public class OrderService {@Autowiredprivate UserService userService;public UserService getUserService() {return userService;}
}
🚗要使@Autowired注解生效,将bean实例化到一级缓存中方法需要改造一下
新增populateBean方法,用来初始bean
private void populateBean(Object bean, Class aClass) {Field[] declaredFields = aClass.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);try {declaredField.set(bean, getBean(declaredField.getName()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}
getBean方法也需要改造一下
这样我们的orderservice里面的userservice就有值了
🚕BeanPostProcessor接口
该接口在显示调用初始化方法的前后添加我们自己的逻辑
public interface BeanPostProcessor {/*** 之前* @param bean* @param beanName* @return*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** 之后* @param bean* @param beanName* @return*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}
我们创建存储BeanPostProcessor的list
在扫描的时候添加BeanPostProcessor
🚖添加自己的BeanPostProcessor
@Component
public class OrderPostProcessor implements BeanPostProcessor {/*** 之前** @param bean* @param beanName* @return*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println(beanName+"执行前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}/*** 之后** @param bean* @param beanName* @return*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println(beanName+"执行后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}
实例化的时候执行BeanPostProcessor逻辑
运行结果
🚛Aop
jdk的动态代理是基于接口生成的代理对象
public interface OrderInterface {void test();
}
在OrderPostProcessor类中加以改造
这样我们就可以动态代理切入我们的orderservice类
以上就是全部内容
实现了以下
- 🚀@ComponentScan注解
- ✈️@Component注解
- 🚁ApplicationContext类
- 🚡@Scope注解
- 🚟BeanDefinition 类
- 🚘@Autowired注解
- 🚕BeanPostProcessor接口
- 🚛Aop
内容,完成了一个超级简单且基础的spring源码
项目源码
博客码了两天,创作不易,多多点赞
相关文章:

【Spring】我抄袭了Spring,手写一套MySpring框架。。。
这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能 文章目录这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能🚀ComponentScan注解✈️Component注解🚁在spring中ioc容器的类是ApplicationConte…...

vue中的生命周期
前言 很多时候我们希望能在 vue 生命周期的过程中执行一些操作,生命周期钩子函数也因此诞生了。相信使用过 vue 框架的同学都知道,生命周期的钩子函数允许我们在实例的不同阶段执行各种操作,便于我们更好的控制和使用实例。 生命周期钩子函数…...

硬件原理图设计规范(二)
1、可编程逻辑器件 编号 级别 条目内容 备注 1 推荐 FPGA的LE资源利用率要保证在50%~80%之间,EPLD的MC资源的利用率要保证在50%~90%之间。对于FPGA中的锁相环、RAM、乘法器、DSP单元、CPU核等资源,经过精确预算,…...

复旦微ZYNQ7020全国产替代方案设计
现在国产化进度赶人,进口的芯片只做了个功能验证,马上就要换上国产的。国内现在已经做出来zynq的只有复旦微一家,已经在研制的有上海安路,还有成都华微(不排除深圳国威也在做,毕竟这个市场潜力很大…...

蓝桥杯真题——自动售水机
2012年第四届全国电子专业人才设计与技能大赛“自动售水机”设计任务书1. 系统框图接下来我们将任务分块: 1. 按键控制单元 设定按键 S7 为出水控制按键,当 S7 按下后,售水机持续出水(继电器接通,指示 灯 L10 点亮&…...

软件质量保证与测试 课程设计 测试报告 缺陷报告撰写方法
测 试 报 告 2020年 6月 1日 测试项目 程序员 测试人 测试阶段: □集成 √系统 □ 测试日志编号清单 1,2,3,4,5,6,7,8,9,10 遗留错误说明:(测试后仍然遗留下来未解决的错误及其说明) 1.系统界面不够友好&…...

vue2和vue3中路由的区别和写法?
前言:Vue 2 和 Vue 3 中路由的主要区别在于使用的路由库不同。在 Vue 2 中,通常使用 Vue Router 作为路由库;而在 Vue 3 中,Vue Router 仍然是官方推荐的路由库,但也可以选择使用新的路由库 - Vue Router Next。下面分…...

【数据结构】第四站:单链表力扣题(一)
目录 一、移除链表元素 二、链表的中间结点 三、链表中倒数第k个结点 四、反转链表 五、合并两个有序链表 六、分割链表 一、移除链表元素 题目描述:力扣 法一:直接循环依次判断 对于这个题目,我们最容易想到的一种思路就是,…...

SAP BPC简介
BPC是SAP在financial application领域主推的产品,由于从原有产品线发展而来,产品本身有两个版本,分别是基于MS OLAP平台和Netweaver OLAP平台。 整个系统分为.net前台和abap后台。由于abap端的数据结构与.net数据结构的差异,所以没…...

Linux网络概述
写咋前面 今天,我们需要初步的认识一下Linux中网络的基本原理,只有大家对这个有一个初步的认识,后面我们学习起来才会更加的简单容易.计算机语言知识那么多,但是Linux不是.面试时,面试官总是会有问题难住你,我们后面需要看看书,这一点非常重要.我们现在谈的是脉络,.是框架.这些…...

Mybatis --- 获取参数值和查询功能
一、MyBatis的增删改查 1.1、新增 <!--int insertUser();--> <insert id"insertUser">insert into t_user values(null,admin,123456,23,男) </insert> 1.2、删除 <!--int deleteUser();--> <delete id"deleteUser">dele…...

【C++】C++入门,你必须要知道的知识
1.C关键字 🔥前言: C是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。熟悉C语言之后,对C学习有一定的帮助。今天的主要目标: 1️⃣ 补充C语言语法的不足&…...

spring(七):事务操作
spring(七):事务操作前言一、什么是事务二、事务四个特性(ACID)三、事务操作(搭建事务操作环境)四、事务操作(Spring 事务管理介绍)五、事务操作(注解声明式事…...

Word怎么转换成PDF文件格式?思路提供
PDF是一种通用的文件格式,它可以在不同操作系统和设备上保持一致的显示效果。在日常工作或学习中,我们常常需要将Word文档转换为PDF格式,以便更好地进行分享、存档或打印,毕竟Word文档则往往会因为不同版本的软件或者字体等原因而…...

HCIE-Cloud Computing LAB备考第二步:逐题攻破--第五题:规划--根据网络平面规划表,完成ensp中接入交换机SW1/2的配置
我是第五题规划题目的要求之一,需要从这里跳转过来,没看过题目的彭友,可以先跳转过去哈 解题:根据网络平面规划表,在两台交换机上对应的端口表填写服务器的网口号,完成ensp中接入交换机SW1/2的配置 答案 完成交换机端口表 第一:心中划分好“服务器接口表”,考试不需…...

【无标题】Perforce研讨会回顾 | Helix Core在芯片行业的应用实例:芯片项目的版本控制、持续集成及自动化
2023年2月28日,龙智联合全球领先的数字资产管理和DevSecOps工具厂商Perforce共同举办Perforce on Tour网络研讨会——“赋能‘大’研发,助力‘快’交付”。 研讨会上,在芯片行业有15年经验的Perforce Helix Core深度用户——何刚了带来精彩演…...

AdamW 优化器
Adam 优化器于 2014 年推出,其思想:既然知道某些参数移动得更快、更远,则每个参数不需要遵循相同的学习率,因为最近梯度的平方代表每一个权重可以得到多少信号,可以除以这个,确保即使是最迟钝的权重也有机会…...

手把手教你基于HTML、CSS搭建我的相册(上)
The sand accumulates to form a pagoda写在前面HTML是什么?CSS是什么?demo搭建写在最后写在前面 其实有过一些粉丝咨询前端该从什么开始学,那当然是我们的前端基础三件套开始学起,HTML、CSS、javaScript,前端的大部分…...

基于Redis实现的延时队列
基于Redis实现的延时队列 针对于Redis实现延时队列有两种实现方式: 使用zset实现实现的延时队列 借助redis zset来实现延时队列,具体的实现代码很简单,就是从zset中取出score小于当前时间戳的数据 import cn.hutool.json.JSONUtil; impor…...

(3.16——3.19)本周后半段总结
周四(3.16) 1.封装了TitleTip组件,并写了博客记录 http://t.csdn.cn/DAY4chttp://t.csdn.cn/DAY4c2.菜单跳转配置完毕,进行了一些页面的细节样式修改 3.基本写完了ServerSideEncryption页面,十一点多剩最后一点交互的…...

C++ 基础: cin和getline() 有啥区别?
所谓温故而知新,所以时不时会回头来看看我们最最基础的知识。 获取标准键盘输入的方法有多种。以C语言来说,最常用的就是cin 和geline() 。那么它们之间有什么区别呢,我们总结一下。 一、cin和geline的异同点 在 C 中,cin 和 ge…...

在使用fastjson中遇到的问题
一、在使用fastjson中遇到的问题 导论:最近在写一个JavaFx项目的时候使用到了fastjson作为处理json数据的依赖。在其它非JavaFx项目中也使用到了相同版本的fastjson,但是可以正常运行,而在JavaFx项目中却报异常,刚开始以为是我的依…...

C++造轮子飙车现场之无锁、有锁环形队列实现
先看带锁的实现。 带锁版本 circular_queue.h // 头文件防卫 #ifndef CIRCULAR_QUEUE_H #define CIRCULAR_QUEUE_H#include <mutex> // 互斥量 #include <condition_variable> // 条件变量template <typename T> class CircularQueue { public:// 构造函数…...

Spring Profiles and @Profile
1. Overview In this tutorial, we’ll focus on introducing Profiles in Spring. Profiles are a core feature of the framework — allowing us to map our beans to different profiles — for example, dev, test, and prod. We can then activate different profiles…...

数据分析-数据探索
文章目录前言主要内容总结更多宝藏前言 😎🥳😎🤠😮🤖🙈💭🍳🍱 随着大数据和人工智能技术的不断发展,数据分析已经成为了一种非常重要的技能和工…...

7个最受欢迎的Python库,大大提高开发效率
当第三方库可以帮我们完成需求时,就不要重复造轮子了 整理了GitHub上7个最受好评的Python库,将在你的开发之旅中提供帮助 PySnooper 很多时候时间都花在了Debug上,大多数人呢会在出错位置的附近使用print,打印某些变量的值 这个…...

Intellij IDEA 中调试 maven 插件
Intellij IDEA 中调试 maven 插件话痨一下步骤1. classfinal-demo 项目部分2. ClassFinal 部分参考资料话痨一下 目前有两个项目: ClassFinal 是一款java class文件安全加密工具。classfinal-demo 是我建的一个Demo,用来测试ClassFinal的加密效果。 目…...

Java全栈知识(1)缓存池
我们先看这么一道题 Integer x new Integer(123); Integer y new Integer(123); System.out.println(x y); // false Integer z 123; Integer k 123; System.out.println(z k); // true Integer a 200; Integer b 200; System.out.println(z k); //false 我们…...

网络安全的特性
0x00 前言 网络安全的特性包括,机密性,完整性,可用性,真实性和不可否认性。详细的内容可以参考如下的内容。 Xmind资源请下载~ 0x01 机密性 机密性(Confidentiality) 意味着阻止未经授权的实体&#x…...

YOLOv8 多目标跟踪
文章大纲 简介环境搭建代码样例跟踪原理代码分析原始老版实现新版本封装代码实现追踪与计数奇奇怪怪错误汇总lap 安装过程报错推理过程报错参考文献与学习路径简介 使用yolov8 做多目标跟踪 文档地址: https://docs.ultralytics.com/modes/track/https://github.com/ultralyt…...