【Spring】我抄袭了Spring,手写一套MySpring框架。。。

news2024/11/25 0:28:28

这篇博客实现了一个简单版本的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.MySpringConfig
                            String sub = filePath.substring(filePath.indexOf("com"), filePath.indexOf(".class"));
                            String classes = sub.replace("\\", ".");
                            Class<?> aClass = classLoader.loadClass(classes);
                            //注册有Component注解的bean
                            if (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注解的bean
        if (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())) {
            //如果是单例,直接返回缓存里的bean
            return this.singletonObjects.get(beanName);
        } else {
            try {
                //如果是多例,直接返回新的bean
                return 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 {
    @Autowired
    private 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
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println(beanName+"执行前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /**
     * 之后
     *
     * @param bean
     * @param beanName
     * @return
     */
    @Override
    public 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源码

项目源码

博客码了两天,创作不易,多多点赞

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

迅为4412开发板Qt 界面切换

本节通过实验介绍通过创建窗口对象的方式实现界面切换&#xff1a; 步骤一&#xff1a; 在主界面 ui 文件添加 pushButton 按钮&#xff0c; 然后新建一个窗口&#xff0c;工程下创建新的 Qt 设计师界面类&#xff0c;如图 &#xff1a; 我们选择 Widget&#xff0c;用户可以根…

logstash+elasticsearch+Kibana(ELK)日志收集

文章目录一.安装elasticsearch二. 安装kibana三.配置logstash四.springboot整合logstash五.spring整合Elastic Search六. 定时清理不要一股脑执行以下语句,请观察修改要修改的地方 注意给logstash,elasticsearch,kibana释放端口,云服务器提供商和系统的端口 一.安装elasticsea…

为什么许多人吐槽C++11,那些语法值得我们学习呢?

致前行的人&#xff1a; 人生像攀登一座山&#xff0c;而找寻出路&#xff0c;却是一种学习的过程&#xff0c;我们应当在这过程中&#xff0c;学习稳定冷静&#xff0c;学习如何从慌乱中找到生机。 目录 1.C11简介 2.统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 …

linux-php 7.3.2安装

1.下载php wget http://am1.php.net/distributions/php-7.3.2.tar.gz 2.解压 tar -zxvf php-7.3.2.tar.gz 3.安装扩展 接下来进行参数配置&#xff0c;配置前如果没有libxml2和libxml2-devel会报错&#xff0c;所以应该更新libxml2并安装libxml2-devel&#xff0c;使用在线…

SpringBoot 项目整合 Redis 教程详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Android Init(后期还会更加完善)

代码分析基于android-12.0.0_r28 前期阶段 kernel/init/main.c&#xff1a; static int __ref kernel_init(void *unused) {// ...省略一堆代码if (execute_command) {ret run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (err…

八百字讲清楚——BCEWithLogitsLoss二分类损失函数

BCEWithLogitsLoss是一种用于二分类问题的损失函数&#xff0c;它将Sigmoid函数和二元交叉熵损失结合在一起。 假设我们有一个大小为NNN的二分类问题&#xff0c;其中每个样本xix_ixi​有一个二元标签yi∈0,1y_i\in {0,1}yi​∈0,1&#xff0c;并且我们希望预测每个样本的概率…

RCNN系列发展历程

1. RCNN RCNN发表于CVPR 2014。是将深度学习应用于目标检测领域的开山之作&#xff0c;凭借卷积神经网络较之传统CV方法的强大特征提取能力&#xff0c;将PASCAL VOC数据集检测率从35.1%提升到53.7%。 RCNN的算法流程如下图所示&#xff0c;其过程主要分为4步&#xff1a; 生成…

Ubuntu中隐藏Apache的版本号等信息

Ubuntu中隐藏Apache的版本号等信息 vim /etc/apache2/apache2.conf在apache主配置文件的末尾增加以下内容&#xff1a; ServerTokens Prod ServerSignature offServerTokens&#xff1a; 默认阀值为Full&#xff0c;以apache-2.0.55为例&#xff0c;阀值可以设定为以下某项&a…

【完整项目开发】Springboot+jsp宠物医院信息管理系统设计和实现--依旧很干

Springbootvue宠物医院信息管理系统设计和实现 **大家好&#xff0c;今天分享最近做的一套系统。**起因源于小伙伴的需求 文末有的获取方式&#xff0c;如需定制系统&#xff0c;需求发来&#xff0c;我为你分忧&#xff0c;搞起 一、 项目介绍 宠物医院信息管理系统是一种…

香港轻量云服务器安全性如何?

​  之前&#xff0c;对于新手外贸站长而言&#xff0c;部署网站首要青睐是香港虚拟主机。但大多人也都清楚&#xff0c;每台香港虚拟主机的资源是由一台物理机分割来的。这无疑会带来一定的安全隐患&#xff0c;比如分割出来的多台香港虚拟主机会共用传统的系统、管理面板以…

Vue3评分(Rate)

可自定义设置以下属性&#xff1a; 是否允许再次点击后清除&#xff08;allowClear&#xff09;&#xff0c;类型&#xff1a;boolean&#xff0c;默认 true 是否允许半选&#xff08;allowHalf&#xff09;&#xff0c;类型&#xff1a;boolean&#xff0c;默认 false star …

Java每日一练(20230417)

目录 1. N 皇后 &#x1f31f;&#x1f31f;&#x1f31f; 2. 搜索二维矩阵 &#x1f31f;&#x1f31f; 3. 发奖金问题 &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 …

神经网络之反向传播算法(自适应矩估计算法Adam变形形式之Adamax、Nadam)

文章目录自适应矩估计算法&#xff08;Adam&#xff09;的两种变形形式1、Adamax算法原理2、Nadam算法原理3、算法实现3.1 Adamax训练过程3.2 Adamax测试过程及结果3.3 Nadam训练过程3.4 Nadam测试过程及结果4、参考源码及数据集自适应矩估计算法&#xff08;Adam&#xff09;的…

打造安全无忧软件应用的十大最佳实践

安全无忧的软件开发最佳实践实在是很有必要&#xff0c;因为安全风险无处不在。在网络攻击盛行的时代&#xff0c;它们可能影响到每个人&#xff0c;包括个人、公司和政府。因此&#xff0c;确保软件开发的安全性至关重要。 本篇文章将解释了什么是安全的软件&#xff0c;如何…

你了解C语言中的柔性数组吗?

本篇博客主要讲解C99中的新语法&#xff1a;柔性数组。 1.什么是柔性数组&#xff1f; 柔性数组就是大小可以变化的数组。 注意跟C99中的变长数组区分开来&#xff0c;变长数组指的是可以使用变量来指定大小&#xff0c;并且不能初始化的数组&#xff0c;比如&#xff1a; i…

实例方法、类方法、静态方法、实例属性、类属性

背景&#xff1a;今天在复习类相关知识的时候&#xff0c;突然想到这几种类型的方法的区别和用法&#xff0c;感觉有点模棱两可&#xff0c;于是总结一下&#xff0c;加深记忆。 定义&#xff1a;想要区别和理解几种方法&#xff0c;首先要定义一个类&#xff0c;要在类中加深…

mysql如何修改时区

mysql 里CST时区的坑 一、 问题简述 mysql里CST时区是个非常坑的概念&#xff0c;因为在mysql里CST既表示中国也表示美国的时区。但是在JDK代码里&#xff0c;CST这个字符串被理解为CenTral Standard Time&#xff08;USA&#xff09;&#xff08;GMT-6&#xff09;&#xff…

java框架都有哪些

Java框架是对Java2中的一些基本概念进行抽象&#xff0c;封装成能被开发者使用的类库&#xff0c;使之能快速开发应用程序。它让开发者能够专注于业务逻辑而不是实现细节。可以说&#xff0c; Java框架是 Java开发中的重要组成部分&#xff0c;它极大地方便了开发者。下面为大家…

腾讯云GPU服务器NVIDIA P40 GPU、P4、T4和GPU自由卡详解

腾讯云GPU云服务器&#xff0c;GPU云服务器实例可选GN8机型、GN6S机型、GN7机型等规格&#xff0c;搭载 NVIDIA P40 GPU&#xff0c;最长可3年&#xff0c;云服务器吧来详细说下腾讯云GPU云服务器&#xff1a; 目录 腾讯云GPU云服务器 腾讯云GPU自由卡 腾讯云GPU云服务器 腾…