Spirng 痛苦源码学习(二)——手写spring大致总框架(一)

news2024/12/23 23:03:00

文章目录

  • 前言
  • 一、总体步骤如下
    • 1、spring 文件夹
    • 2、myProject 文件夹
  • 二、主要coding
    • 1、配置文件
    • 2、容器
    • 3、一些spring中的重要的注解
    • 4、项目中的使用
    • 5.重要的bean定义信息
    • 6、postProcessor重要,前置通知和后置
  • 主要项目的截图


前言

本文主要基于spring的注解的方式完成spring总体流程

  • 1、配置文件配合容器实现
  • 2、通过注解扫描所有的bean定义信息
  • 3、完成CreatBean方法
  • 4、实现getBean方法
  • 5、实现postProcessor完成AOP(使用JDK动态代理)
  • 6、实现aware回调

一、总体步骤如下

总的来说
1、使用了spring文件夹模拟了我们平常的依赖。
2、project文件夹模拟我们平常的项目。

1、spring 文件夹

简介=》用于模拟我们导的spring依赖

  • 1、编写一个容器

  • 2、简单的完成一个构造方法,支持传入配置文件,通过注解方式进行下面的操作
    ——以上第一步完成存取——

  • 3、定义一个@ComponentScan注解,目的是为了扫描

  • 4、在context容器构造器中,扫描配置文件的注解,得到其中的路径值

  • 5、扫描对应路径下的所有的类(我们扫描的是.class文件,而不是.java);所以我们要拿到的是应用类加载器,然后获取对应的路径下的文件

——以上是类扫描——

  • 6、获取所有文件的绝对路径,然后截取成可以读取到对应类的值
  • 7、读取对应的类上的注解(如@Component,@Service…)判断是不是一个bean

——以上是判断是不是一个bean的逻辑——

  • 8、定义@Scope注解(定义这是一个单例还是一个原型)
  • 9、判断是不是有@Scope注解,取到其中的值
  • 10、将类信息,单例or原型等信息封装成一个bean的定义信息,放到map中

——以上扫描包结束,封装bean定义信息放map中——

  • 11、根据bean定义信息,创建bean,然后放到singletonObjects中

——以上创建bean到单例池中——

  • 12、定义@Autowired依赖注入注解

  • 13、判断bean类中是不是有对应的属性值有依赖注入注解

  • 14、有点话就先getBean去单例缓存池中拿,拿不到就CreatBean(判断单例和原型)
    ——以上是依赖注入的过程——

  • 15、定义后置处理器接口:里面主要分为前置和后置处理postProcessBeforeInitialization、postProcessAfterInitialization

  • 16、在扫描的时候,就把所有的后置处理器干到缓存池中

  • 17、判断这个bean是不是PostProcess(instanceof 父类就行)

  • 18、在创建bean的时候我们要取出所有缓存中的处理器,然后看看他们要干嘛(循环每一个后置处理器)
    ——以上定义后置处理器接口——

  • 19、定义@XXAware回调接口

  • 20、和后置处理器差不多,我们也才creatBean判断bean是不是Aware

  • 21、有的话就让spring回调干点事情

2、myProject 文件夹

简介=》用于我们项目中利用spring

  • 1、编写配置文件
  • 2、编写启动类
    • 2.1 获取一个容器,然后在容器中getBean

——以上第一步完成存取——

  • 3、和spring配置一样,我们要在配置文件中配置@ComponentScan注解

——以上是类扫描——

  • 4、在对应的类中添加@Scope注解

——以上扫描包结束,封装bean定义信息放map中(区分原型还是单例)——

  • 5、在对应类中添加@Autowired注解即可,其他交给容器去整
    ——以上是依赖注入的过程——

  • 6、自定义一个BeanPostProcess,我们的Aop其实就是这里搞起来的,读取aop的注解缓存池,然后一通前置通知get
    ——以上定义后置处理器接口——

二、主要coding

1、配置文件

package com.xusj.myProject.config;

import spring.annotation.ComponentScan;

/**
 * @author xusj
 * <br>CreateDate 2022/11/29 0:30
 */
@ComponentScan("com.xusj.myProject")
public class AppConfig {

}

2、容器

package spring.context;

import spring.annotation.Autowired;
import spring.annotation.Component;
import spring.annotation.ComponentScan;
import spring.annotation.Scope;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author xusj
 * <br>CreateDate 2022/11/29 0:29
 */
public class MyApplicationContext {
    private Class<?> configClass;

    /**
     * bean定义信息的缓存池
     * k
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    private final Map<String, Object> singletonObjects = new HashMap<>();


    /**
     * 构造方法,通过配置类完成
     *
     * @param clazz
     */
    public MyApplicationContext(Class<?> clazz) {
        this.configClass = clazz;
        // 扫描对应下的所有子包
        // 1、判断有没有注解
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            // 2、先拿到注解和对应的值(路径)
            ComponentScan componentScanAnnotation = clazz.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            // 转化成我们需要的path
            path = path.replace(".", "/");
            // 3、获取对应的类加载器,我们需要读取的应用类加载器中的文件而不是我们写的.java文件[返回类的类装载器。]
            ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
            // 4、获取对应路径的资源
            URL resource = classLoader.getResource(path);
            // 5、获取对应位置的文件
            File file = new File(resource.getFile());
            // 判断文件
            if (file.isDirectory()) {
                getFile(file);
            }


        }

    }

    /***
     * 获取所有的文件
     *
     * @param file 文件
     * @author xusj
     * <br>CreateDate 2022/11/29 1:05
     */
    private void getFile(File file) {
        // 遍历文件(一直遍历到最后)
        for (File f : Objects.requireNonNull(file.listFiles())) {
            if (f.isDirectory()) {
                // 递归
                getFile(f);
            } else {
                // 扫描所有类判断是不是个bean,是的话就创建一个bean定义信息,然后放到map中
                scanComponent(f);
            }
        }

        // 扫描bean定义信息map缓存,创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            // 判断是单例还是原型
            if ("singleton".equals(beanDefinition.getScope())) {
                // 创建bean
                Object bean = createBean(beanName, beanDefinition);
                // TODO XUSJ 放到单例池中
                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * 创建bean
     *
     * @param beanName
     * @param beanDefinition
     */
    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        // 获取类信息
        Class<?> clazz = beanDefinition.getType();

        // 通过构造方法创建bean
        Object instance = null;
        try {
            instance = clazz.getConstructor().newInstance();

            // 判断依赖注入的问题
            for (Field field : clazz.getDeclaredFields()) {
                // 判断该类中有没有@Autowired
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(instance, getBean(field.getName()));
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scanComponent(File f) {
        // 1 获取所有的文件
        // 获取文件的绝对路径
        String absolutePath = f.getAbsolutePath();
        // 更换成可应用类加载器可以处理的结果
        absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
        absolutePath = absolutePath.replace("\\", ".");
        // 2 通过应用类加载器获取绝对路径中的类
        System.out.println("对应文件的绝对路径=》" + f.getName() + ":" + absolutePath);
        ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
        try {
            Class<?> clazz = classLoader.loadClass(absolutePath);
            // 3 判断这个类上面是不是有对应的bean相关的注解
            if (clazz.isAnnotationPresent(Component.class)) {
                // 4 获取我们定义的beanName
                Component componentScanAnnotation = clazz.getAnnotation(Component.class);
                String beanName = componentScanAnnotation.value();
                // 如果没有定义的话,直接就使用类目作为beanName
                if (beanName.isEmpty()) {
                    // 获取简单类目
                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                }
                // 5 构建bean的定义信息
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setType(clazz);
                beanDefinition.setBeanName(beanName);
                // 6、通过注解判断是单例还是原型
                if (clazz.isAnnotationPresent(Scope.class)) {
                    Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                    String value = scopeAnnotation.value();
                    // 放到bean的定义信息中
                    beanDefinition.setScope(value);
                } else {
                    // 默认为singleton
                    beanDefinition.setBeanName("singleton");
                }
                // 将bean定义信息,放到beanDefinitionMap中
                beanDefinitionMap.put(beanName, beanDefinition);
            }


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /***
     * 通过beanName去获取bean
     *
     * @param name beanName
     * @return {@link Object}
     * @author xusj
     * <br>CreateDate 2022/11/29 0:34
     */
    public Object getBean(String name) {
        // 先判断bean定义信息是不是有值
        if (!beanDefinitionMap.containsKey(name)) {
            throw new RuntimeException();
        }
        // 获取bean定义信息
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);

        // 判断是不是单例
        if ("singleton".equals(beanDefinition.getScope())) {
            // 先去单例池中去拿
            Object singletonObj = singletonObjects.get(name);
            if (singletonObj == null) {
                // 没有说明还没创建,那我就创建一个
                singletonObj = createBean(name, beanDefinition);
                // 放到单例池中
                singletonObjects.put(name, singletonObj);
            }
            return singletonObj;
        } else {
            // 原型模式[就是直接搞一个新的出来]
            Object prototypeBean = createBean(name, beanDefinition);
            return prototypeBean;
        }
    }
}

3、一些spring中的重要的注解

在这里插入图片描述

4、项目中的使用

package com.xusj.myProject.service;

import spring.annotation.Autowired;
import spring.annotation.Component;
import spring.annotation.Scope;
import spring.aware.MyBeanAware;

/**
 * @author xusj
 * <br>CreateDate 2022/11/29 0:35
 */
@Component(value = "oneService")
@Scope(value = "singleton")
public class OneService implements MyBeanAware {

    @Autowired
    private TwoService twoService;

    public void test() {
        System.out.println("twoService" + "+++" + twoService);
    }

    @Override
    public void setSth() {

    }
}

5.重要的bean定义信息

package spring.context;

/**
 * @author xusj
 * <br>CreateDate 2022/11/29 10:02
 */
public class BeanDefinition {
    /**
     * beanName
     */
    private String beanName;

    /**
     * 类信息
     */
    private Class<?> type;


    /**
     * bean类型
     */
    private String scope;

    public BeanDefinition() {
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "beanName='" + beanName + '\'' +
                ", type=" + type +
                ", scope='" + scope + '\'' +
                '}';
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

}

6、postProcessor重要,前置通知和后置

依赖倒置原则,先定义接口

package com.xusj.myProject.postProcessor;

import spring.annotation.Component;
import spring.processor.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 我的后置处理器
 *
 * @author xusj
 * <br>CreateDate 2022/11/29 16:31
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    // 前置搞点事情(Aop,这里用的是JDK的动态代理,spring使用的CGLIB)
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 其实AOP也是在这里整的,就是读一些注解然后整
        if ("userService".equals(beanName)) {

            return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 切面
                    System.out.println("切面逻辑");
                    // 原来的对象去执行方法
                    return method.invoke(bean, args);
                }
            });
        }

        // bean
        return bean;
    }

    // 后置搞点事情
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

7、aware回调接口
总的来说和postProecssor一样,也是依赖倒置原则

主要项目的截图

在这里插入图片描述

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

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

相关文章

Spring相关源码解读

框架1.ApplicationContext refresh的流程2.spring bean 的生命周期3.spring事务失效的几种场景以及原因4.springMVC执行流程5.一些注解&#xff08;1&#xff09;Configuration&#xff08;2&#xff09;Import&#xff08;3&#xff09;SpringBootApplication6.spring中有哪些…

BP神经网络详解,Python实现求解异或问题

BP神经网络 符号及其含义 nln_lnl​表示第lll层神经元的个数&#xff1b;f(⋅)f()f(⋅)表示神经元的激活函数&#xff1b;W(l)∈Rni∗ni−1W^{(l)}\in\mathbb R^{n_i*n_{i-1}}W(l)∈Rni​∗ni−1​表示第l−1l-1l−1层到第lll层的权重矩阵&#xff1b;wij(l)w_{ij}^{(l)}wij(l…

基于tensorflow的ResNet50V2网络识别动物

前言 之前很多人在&#xff0c;如何进行XXX的识别&#xff0c;对应的神经网络如何搭建。对应神经网络怎么搭建&#xff0c;我也是照本宣科&#xff0c;只能说看得懂而已&#xff0c;没有对这块进行深入的研究&#xff0c;但是现在tensorflow&#xff0c;paddle这些工具&#x…

长期稳定的项目—steam搬砖

大家好&#xff0c;我是阿阳 steam搬砖项目一直稳稳定定的进行着&#xff0c;有些朋友基本都观察了近2年 所以很多人问我公众号的项目是不能做了吗&#xff1f;怎么最近做新的去了&#xff1f;很明显这是几乎不可能的事情&#xff0c;steam做2年了&#xff0c;本公众号都能翻到…

这几个数据分析项目,让我看到了什么才叫专业!!

大家好&#xff0c;我是小一 新的一周又来了&#xff0c;从今天开始&#xff0c;会出一个新的系列《数分实验室》 实验室会介绍一些有内核、有科技的数据分析实战项目。 项目数据集、源代码都是公开的&#xff0c;非常适合想练手但是又没数据、没参考案例的同学 今天先热热…

ES的基础概念

1、ES是什么 Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单&#xff0c;它不仅包括了全文搜索功能&#xff0c;还可以进行以下工作:分布式实时文件存储&am…

6-1分支限界法

6-1分支限界法 1.分支限界法与回溯法的不同 &#xff08;1&#xff09;求解目标: 回溯法的求解目标是找出解空间树中满足约束条件的所有解&#xff08;或一个最优解&#xff09;&#xff0c; 而分支限界法的求解目标则是找出满足约束条件的一个解&#xff08;或最优解&#x…

组织机器学习代码

组织机器学习代码 从note本转移到 Python 脚本时组织代码。 Intuition 有组织的代码就是有可读的、可重现的、健壮的代码。您的团队、经理&#xff0c;最重要的是&#xff0c;您未来的自己&#xff0c;将感谢您为组织工作付出的最初努力。在本课中&#xff0c;将讨论如何将代码…

pytest测试框架入门1

pytest单元测试框架 单元测试是指在软件开发当中&#xff0c;针对软件的最小单位&#xff08;函数&#xff0c;方法&#xff09;进行正确性的检查测试 单元测试框架主要做什么 测试发现&#xff1a;从多个文件里面找到我们的测试用例测试执行&#xff1a;按照一定的顺序和规则…

初学者指南: 使用NumPy数组进行图像处理

这里写自定义目录标题初学者指南: 使用NumPy数组进行图像处理1、加载图像2、裁剪图像3、分离颜色4、转换5、灰度转换6、图像分割结语初学者指南: 使用NumPy数组进行图像处理 由于图像也可以被视为由数组组成&#xff0c;因此我们也可以使用NumPy执行不同的图像处理任务。在本文…

【Lilishop商城】No2-6.确定软件架构搭建五(本篇包括定时任务xxl-job)

仅涉及后端&#xff0c;全部目录看顶部专栏&#xff0c;代码、文档、接口路径在&#xff1a; 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇只介绍重点架构逻辑&#xff0c;具体编写看源代码就行&#xff0c;读起来也不复杂~ 谨慎&#xf…

如何配置一台适合oc渲染器的电脑?

众所周知&#xff0c;Octane 是最流行的渲染引擎之一。此外&#xff0c;Octane 是一个 GPU 渲染引擎&#xff0c;它使用一种计算最终生成的图片的方法&#xff0c;试图达到照片般的真实感。Octane 是一种利用 GPU 技术的无偏渲染引擎&#xff0c;非常接近物理精度。一台好的 PC…

计算机组成原理习题课第三章-2(唐朔飞)

计算机组成原理习题课第三章-2&#xff08;唐朔飞&#xff09; ✨欢迎关注&#x1f5b1;点赞&#x1f380;收藏⭐留言✒ &#x1f52e;本文由京与旧铺原创&#xff0c;csdn首发&#xff01; &#x1f618;系列专栏&#xff1a;java学习 &#x1f4bb;首发时间&#xff1a;&…

天宇优配|平台助企“抱团出海” “小而美”中觅“先机”

天津华图轿车物流有限公司一批二手新能源车从连云港装船发往阿联酋迪拜。&#xff08;采访方针供图&#xff09; 最近&#xff0c;一笔100.8万美元的出口信誉稳妥保单融资借款&#xff0c;被划到了天津华图轿车物流有限公司的账户上。正值客户“下单”高峰期&#xff0c;这笔及…

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)&#xff08;一&#xff09; 本文目录&#xff1a; 一、【旋转的精灵女孩】案例运行效果 二、Three.js简介 三、Three.js代码正常运行显示条件 &#xff08;1&#xff09;不载入任何纹理贴图的网页 &#xff08;2&…

双十二蓝牙耳机啥牌子好?2022年度热销蓝牙耳机排名

这期双十二数码好物分享&#xff0c;工作室打算来跟大家说说蓝牙耳机这个话题&#xff0c;它已经成为出行必带的装备&#xff0c;上班族、学生党、游戏党都离不开蓝牙耳机。今年我们测评过数十款型号了&#xff0c;本期我们盘点了今年热销的蓝牙耳机排名&#xff0c;让大家直观…

【学习笔记】《Python深度学习》第五章:深度学习用于计算机视觉

文章目录1 卷积神经网络简介1.1 卷积运算1.2 最大池化运算2 在小型数据集上从头开始训练一个卷积神经网络2.1 下载数据2.2 构建网络2.3 数据预处理2.4 数据增强3 使用预训练的卷积神经网络3.1 特征提取3.2 微调模型3.3 小结4 卷积神经网络的可视化4.1 可视化中间激活4.2 可视化…

新手想开一个传奇该如何操作?开一个传奇必须掌握哪些知识要点

对于这个问题&#xff0c;近期问的人比较多&#xff0c;相比这也是热爱传奇这个游戏的朋友会问到的一个问题&#xff0c;因为喜欢玩这个游戏&#xff0c;也想要自己去开一个 经营一个 不管是电脑端也好 还是手机端也好&#xff0c;但是对于一些新手确实不知道该如何开始操作 从…

H3C opsf/rip/ftp/telent/nat/acl综合

实验拓扑 拓扑下载 https://sharewh2.xuexi365.com/share/84b85b32-acb7-4f62-a389-6188680a19f3?t3 图 1-1 注&#xff1a;如无特别说明&#xff0c;描述中的 R1 或 SW1 对应拓扑中设备名称末尾数字为 1 的设备&#xff0c;R2 或 SW2 对应拓扑中设备名称末尾数字为 2 的设备…

三天入门Redis【快速浏览版】

文章目录第一天1.1 Redis基础1.1.1 NoSql引入1.1.2 NoSql特点1.1.3 NoSql数据库1.1.4 Redis概述1.1.5 Redis文件的作用1.1.6 Redis相关介绍1.2 常用的五大类型及操作⭐️1.2.1 Redis键&#xff08;key&#xff09;1.2.2 库的一些操作1.2.3 Redis字符串1.2.4 Redis列表&#xff…