# Sbring **Repository Path**: swis/sbring ## Basic Information - **Project Name**: Sbring - **Description**: 极简springioc仿写 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-04-12 - **Last Updated**: 2023-04-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README spring基本原理的简单实现,极其简易 跟着视频做的,下面有链接,很基础 # 介绍 简单实例了spring的扫描,依赖注入功能 涉及到的源码类如下 使用java1.8 maven工程 ![image.png](assets/源码图.png) ## [准备工作](https://www.bilibili.com/video/BV1p84y1s7WG?p=3&vd_source=474909108c3b4cc590fbc82b56819d92) 创建两个包,一个放spring的源码,一个是用户的源码,使用spring框架 Test为启动类,启动类中需要获得Spring容器,传入配置类,获得bean 因此创建了两个注解用于扫描和添加bean ![image.png](assets/image.png) ## [扫描逻辑](https://www.bilibili.com/video/BV1p84y1s7WG/?p=4&spm_id_from=pageDriver&vd_source=474909108c3b4cc590fbc82b56819d92) 我们在springApplicationContext(容器类)中传入了一个配置类,在容器中需要对其进行解析。 解析方式主要是针对spring提供的注解进行解析 > `getDeclaredAnnotation` 得到的是当前成员的注解,不包括继承的,`getAnnotation`得到的包括继承的 1. 通过componentScan注解拿到扫描路径(包名,使用.) 2. 获得路径下的类文件 通过应用类加载器拿到资源(注意是路径,使用/) 将资源转成文件处理 3. 判断类文件是否有spring提供的@Component注解(还有@scope注解) 4. 区分单例和原型 创建了一个**beandefinition**类 添加了一个Scope注解 > BeanDefinition:表述一个bean,包含bean所具有的信息,相当于是bean的一个规范 > ![image.png](assets/image2.png) 创建了两个map,一个存放单例,一个存放扫描进来的bean的定义 5. 通过beandefinitionMap来创建单例bean ## [依赖注入](https://www.bilibili.com/video/BV1p84y1s7WG/?p=6&spm_id_from=pageDriver&vd_source=474909108c3b4cc590fbc82b56819d92) 从IoC容器中获得bean ![image.png](assets/autowired.png) 首先启动的第一步进行扫描,然后初始化单例池 在创建每一个实例的时候,都会扫描当前类的成员变量(Field)是否添加了`@Autowired`注解,如果有则为该变量赋值 在创建的时候就对其成员变量赋值了 > 这里我有些地方不明白,万一要注入的实例还没被创建怎么办 ![image.png](assets/依赖注入.png) ## [Aware回调](https://www.bilibili.com/video/BV1p84y1s7WG/?p=7&spm_id_from=pageDriver&vd_source=474909108c3b4cc590fbc82b56819d92) ![image.png](assets/aware.png) 创建接口,实现方法 ![image.png](assets/beanNameAware.png) ![image.png](assets/实现beanNameAware.png) 相当于另一种形式的依赖注入,判断方法由根据注解判断换成根据接口来判断 ```java // aware回调 if( instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } ``` 类似的还有`initializingBean`,初始化bean,同样的创建bean的时候回调接口的方法 相当于spring在创建bean的时候提供的钩子方法,对外的构造方法之类的 ## [BeanPostProcessor](https://www.bilibili.com/video/BV1p84y1s7WG/?p=9&spm_id_from=pageDriver&vd_source=474909108c3b4cc590fbc82b56819d92) spring向外提供的一个扩展机制,也是通过接口回调 包括其子接口提供的方法有,实例化前,实例化后,初始化前,初始化后,回调之前,回调之后 ![image.png](assets/beanpostprocessor.png) spring提供的接口,程序员如果要使用的话,在自己的类中实现`BeanPostProcessor`,该类要使用`@Component`注解 首先要让Spring扫描到实现接口的类,在扫描的过程中进行判断当前类是否实现了该接口 ```java // 判断当前class是否实现BeanPostProcessor接口 if (BeanPostProcessor.class.isAssignableFrom(clazz)) { // 得到BeanPostProcessor对象,进行保存 BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance(); beanPostProcessorList.add(instance); } ``` 是则创建一个实例对象,将它保存在list中,在创建类的时候获得该对象,调用对象的方法 在创建类的时候调用 ```java for (BeanPostProcessor bpp : beanPostProcessorList) { bpp.postProcessBeforeInitialization(instance, beanName); } ``` 这里可以看到,其实在每个bean被创建的时候都会调用bpp对象的方法 其实就是在实例化bean的时候给bean添加一些行为,每写一个接口就是在bean添加行为,只是说,会给所有的bean添加 ![image.png](assets/bpp.png) 这里我自己创建了两个接口,都实现了`BeanPostProcessor` ![image.png](assets/bpp2.png) 事实上BeanPostProcessor本身就是一个单例bean,因为它的实现类加了@Component注解 本来应该要从单例池中拿到bpp对象再添加到bppList中的,这里是直接实例化里一个放进去了 > 这里只是简单的实现了原理,但是如果正的从单例池里面拿的话还是会出现**对象被创建的先后次序**问题 > > 传说中的**循环依赖** 关于bpp的执顺序,因为是遍历数组,可以根据自己的需要设计优先级实现排序 前面简单的实现了spring的基本原理,aop是基于BeanPostProcessor实现的,大致上就是在创建bean实例的过程中对其进行动态代理,直接返回生成的代理对象