手写模拟一个简单的Spring(上)

0、目的

为了能更好的学习和理解Spring,所以这里斗胆简单手写模拟一个Spring,既然是简单模拟,那么重心就是放在Spring的整个流程上。一些比较复杂的方法这里就轻量级简单实现,主要是突破下自己的思维的局限。

1、Spring流程概览

a、Spring的底层源码启动过程
b、BeanDefinition和BeanPostProcessor的概念
c、Spring解析配置类等底层源码工作流程
d、SpringAOP的底层源码工作流程

2、手写基础功能-- Spring的扫描逻辑

接下来我们就根据Spring的流程来开始我们的手写。

2.1、经典的Spring三行代码

请添加图片描述
这三行代码相信大家学过Spring的都不陌生吧。
第一行 初始化Spring容器,完成扫描、注册非懒加载的单例bean等功能
第二行 从Spring容器中获取bean,可能是直接拿也可能是创建。
第三行 调用对象的方法

那我们就仿造Spring的方式,先按照这个方式来开始动手。

2.1.1、 项目结构

请添加图片描述
在scdn包下面:
spring包代表我们模拟写的纯Spring内部相关代码

year包为我们的项目代码(大家在自己实践的时候也可以分别建成两个项目,业务模块依赖自己写的spring包模块,也有同样的效果)

service包中就是等待注入的bean。

三行代码已经模拟完成,这个时候运行肯定是会报错的。
请添加图片描述

2.2、包路径扫描

2.2.1、配置扫描路径

定义一个扫描路径的注解
在这里插入图片描述

在配置类中配置扫描路径
请添加图片描述

2.2.2、解析扫描路径

接下就是重头戏,如何来解析传过来的路径?

先拿到注解中的传过来的扫描路径。
请添加图片描述
这里需要用到类加载机制相关知识了。传送门。。待完善

我们直接用AppClassLoader获取到项目的根目录,则可以直接访问到对应的文件夹下。


public YearApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 判断注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan scanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String scanPath = scanAnnotation.value();
            // 这里需要转为文件夹访问路径
            scanPath = scanPath.replace(".", "/");

            // 拿到类加载器
            ClassLoader classLoader = YearApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(scanPath);
            // 取到扫描路径对应到文件夹
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                for (File f : file.listFiles()) {
                    String absolutePath = f.getAbsolutePath();
                    // 文件对应到绝对路径 /target/classes/com/scdn/year/service/UserService.class
                    // 需要转为可被类加载器加载的路径
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("/", ".");

                    // 用类加载器来加载类,并且判断是不是要注册成bean
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);

                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

代码中关键步骤都有对应的注释,这里就不做过多说明了。
这里引出来一个问题:什么样的类需要注册成为bean?

我数三个数。 3、2、1

哈哈,这里用比较简单的方式来处理,只有类上有我们认定的注解,就认为这个类需要注册成为bean。

我们就自己定义一个@Component注解,用来判断是否需要注册成为bean。

2.2.3、得到需要注册bean的类

先定义一个注解
请添加图片描述

在要注入的类上加我们自定义的注解,为了做区分,另一个类我们就不加,也方便做对照

请添加图片描述

大家可以看到,这里只把加了注解的类打印出来,大家也可以自行尝试下。

请添加图片描述

3、手写基础功能-- Spring的扫描逻辑

这里是下集预告,就这两天马上出下集,敬请各位期待。整体代码完成后源码会上传gitee仓库

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值