手写MiniSpring--- IOC

手把手带你写一个MiniSpring

最近学习极客时间上的课程,记录一下。

实现Spring IoC

1 原始IoC

首先是Bean Factory,存在的意义是将创建对象与实用对象的业务代码解耦,让业务开发人员无需关注底层对象(Bean)的构建和生命周期管理,专注于业务开发。

第一步,原始 IoC 容器:

  1. BeanDefinition → bean在内存中的映像

  2. 读取XML,获取Bean配置

  3. 反射,创建Bean

  4. Map 保存Bean,并提供给外部使用

相关类

  • BeanDefinition:对应Bean的定义,具有id和className

  • ClassPathXmlApplicationContext:解析某个路径下的 XML 来构建应用上下文

    • readXml 方法,来获取 XML 内的信息

    • instanceBeans 方法,实力化Bean,并放到Map中构建ID与实际类的映射关系。

⚠️此时的Context 兼具了 BeanFactory 的功能

第二步,解耦ClassPathXmlApplicationContext

根据设计模式

单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】: 一个类负责一项职责。

所以要对Context进行功能拆分,在此之前首先梳理Context主要功能。

ClassPathXmlApplicationContext主要功能:

  • 一是提出一个最基础的核心容器

  • 二是把 XML 这些外部配置信息的访问单独剥离出去,便于其他数据源的扩展。

相关类:

  • BeanFactory接口

    • getBean:获取一个Bean。 实例化Bean,并放到Map中构建映射关系。

    • registerBeanDefinition:注册一个 BeanDefinition。管理其生命周期。

  • SimpleBeanFactory:简单实现类

  • Resource接口

    • 把外部的配置信息都当成 Resource(资源)来进行抽象。

  • ClassPathXmlResource

    • 读取的都是 XML 文件配置,另外也可以指定其他数据源

  • XmlBeanDefinitionReader

    • 将解析好的 XML 转换成 BeanDefinition,并注册到 BeanFactory 中。

此时的ClassPathXmlApplicationContext:内部功能以及对应实现类

  • 解析 XML 文件中的内容。-- ClassPathXmlResource

  • 加载解析的内容,构建 BeanDefinition。-- XmlBeanDefinitionReader

  • 读取 BeanDefinition 的配置信息,实例化 Bean,然后把它注入到 BeanFactory 容器中。-- SimpleBeanFactory

2 扩展Bean

上一步实现了原始的IoC容器,下面继续扩展增强 IoC 容器

主要扩展功能如下:

  • 增加单例 Bean 的接口定义,然后把所有的 Bean 默认为单例模式

  • 预留事件监听的接口,方便后续进一步解耦代码逻辑。

  • 扩展 BeanDefinition,添加一些属性,现在它只有 id 和 class 两个属性,需要进一步完善。

构建单例Bean

首先通过扩展SimpleBeanFactory来实现单例创建管理

扩展前 VS 扩展后

SimpleBeanFactory 实现了 BeanDefinitionRegistry,这样 SimpleBeanFactory 既是一个工厂同时也是一个仓库

BeanFactory和SingletonBeanRegistry这两个接口,角色分离。一个是工厂,另一个是仓库。

扩展前
扩展后-单例Bean

下面是Java类的UML关系图,最明显的是属性字段的变更

SimpleBeanFactory之前拥有beanDefinitions, singletons,beanNames三个字段

后面进行拆分

DefaultSingletonBeanRegistry: singletons,beanNames

SimpleBeanFactory: beanDefinitionNames, beanDefinitionMap

扩展前

扩展后

增加事件监听

  • ApplicationEvent :用于监听应用的事件

  • ApplicationEventPublisher:发布事件

注入

注入操作的本质,就是给 Bean 的各个属性进行赋值

  • Setter 注入

    • ArgumentValue

    • ArgumentValues

  • 构造器注入

    • PropertyValue

    • PropertyValues

扩展 BeanDefinition

扩展 BeanDefinition 的属性,在原有 id 与 name 两个属性的基础上,

新增 lazyInit、dependsOn、initMethodName 等属性。

扩展前
扩展后

集中存放 BeanDefinition

新增 BeanDefinitionRegistry 接口。它类似于一个存放 BeanDefinition 的仓库,可以存放、移除、获取及判断 BeanDefinition 对象

调整 BeanFactory,新增 Singleton、Prototype 的判断,获取 Bean 的类型。

3 依赖注入

给Bean注入值并解决循环依赖问题

这一章节要解决的核心是循环依赖问题

Mini-Spring执行步骤:

  • 文中先读取配置文件并创建所有的BeanDefinition

  • 然后创建毛坯bean放入earlySingletonObjects

  • 最后调用refresh方法,对所有的BeanDefinition执行getBean

  • 在getBean中进行组装注入依赖,并且注册进入singletonObjects

三级缓存:(Mini-Spring相当于两级缓存)

  • 毛胚bean

    • earlySingletonObjects:用于存储早期创建但未完成初始化的单例bean实例。即毛坯

  • 补齐属性。所有的毛胚bean都是提前创建出来的,后面面对循环依赖的时候,拿到的是这个提前准备好的毛胚bean。

    • singletonObjects:用于存储完全创建好的单例bean实例。

  • 在Spring里面是一个factory产生的,文中是直接创建的。

    • singletonFactories:用于存储创建单例bean实例的工厂对象。(Spring中)

核心代码:

public Object getBean(String beanName) throws BeansException{
    // 从singletonObjects中查找bean
    Object singleton = this.getSingleton(beanName);
    if (singleton == null) {
        //从earlySingletonObject中查找bean
        singleton = this.earlySingletonObjects.get(beanName);
        if (singleton == null) {
            BeanDefinition bd = beanDefinitionMap.get(beanName);
            //组装创建bean
            singleton = createBean(bd);
            //注册进singletonObjects
            this.registerBean(beanName, singleton);
        }
    }
    return singleton;
}

4 IOC支持注解

这里调试时候发现了一个问题:

注入是通过属性名去查找对应的bean的,即下面的bbs

@AutoWired 
private BaseBaseService bbs;

而查找bean毛坯是在earlySingletonObjects,其中的bean名称(basebaseservice)是从xml配置中读取来的

<bean id="basebaseservice" class="com.minis.test.BaseBaseService" init-method="init"></bean>

两者不一致则会导致查找不到对应的bean,从而无法注入注解属性。

将两者调整一致则程序可以正常运行。

注解支持

通过AutowiredAnnotationBeanPostProcessor进行解析属性名,并进行注入

这里需要明确的是,这一步是在所有毛坯bean生成之后进行的步骤,下面在getBean方法中会有解析。

public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor {
    private AutowireCapableBeanFactory beanFactory;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Object result = bean;
        Class<?> clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if(fields!=null){
            //对每一个属性进行判断,如果带有@Autowired注解则进行处理
            for(Field field : fields){
                boolean isAutowired = field.isAnnotationPresent(Autowired.class);
                if(isAutowired){
                    //根据属性名查找同名的bean
                    String fieldName = field.getName();
                    Object autowiredObj = this.getBeanFactory().getBean(fieldName);
                    //设置属性值,完成注入
                    try {
                        field.setAccessible(true);
                        field.set(bean, autowiredObj);
                        System.out.println("autowire " + fieldName + " for bean " + beanName);
                    } }}}
        return result;}}

public Object getBean(String beanName) throws BeansException{
    // 先尝试直接从容器中获取bean实例,从singletonObjects中获取
    Object singleton = this.getSingleton(beanName);
    if (singleton == null) {
        //如果没有实例,则尝试从毛胚实例中获取
        singleton = this.earlySingletonObjects.get(beanName);
        if (singleton == null) {
            //如果连毛胚都没有,则创建bean实例并注册
            System.out.println("get bean null -------------- " + beanName);
            BeanDefinition bd = beanDefinitionMap.get(beanName);
            singleton=createBean(bd);
            this.registerBean(beanName, singleton);
            //进行beanpostprocessor处理
            //step 1 : postProcessBeforeInitialization
            applyBeanPostProcessorsBeforeInitialization(singleton, beanName);
            //step 2 : init-method
            if (bd.getInitMethodName() != null && !bd.getInitMethodName().equals("")) {
                invokeInitMethod(bd, singleton);
            }
            //step 3 : postProcessAfterInitialization
            applyBeanPostProcessorsAfterInitialization(singleton, beanName);
        }
    }
    if (singleton == null) {
        throw new BeansException("bean is null.");
    }
    return singleton;
}

新的BeanFactory

这里舍弃了原始的SimpleBeanFactory,使用AbstractBeanFactory 代替,同时用AutowireCapableBeanFactory实现,并扩展了注解处理能力

抽象接口类模式

提取BeanFactory 接口,定义一个抽象的 AbstractBeanFactory。通过这个抽象类,将 Bean 工厂需要做的事情的框架搭建出来,然后在具体实现类中完善细节。这种程序结构称为 interface-abstract-class(接口抽象类),是一种做框架时常用的设计模式。

类关系图如下:

之前
之后

执行流程

  1. 启动 ClassPathXmlApplicationContext 容器,执行 refresh()。

  2. 在 refresh 执行过程中,调用 registerBeanPostProcessors(),往 BeanFactory 里注册 Bean 处理器,如 AutowiredAnnotationBeanPostProcessor。

  3. 执行 onRefresh(), 执行 AbstractBeanFactory 的 refresh() 方法。

  4. AbstractBeanFactory 的 refresh() 获取所有 Bean 的定义,执行 getBean() 创建 Bean 实例。

  5. getBean() 创建完 Bean 实例后,调用 Bean 处理器并初始化。

5 构建工厂体系

接口隔离原则

在 Java 语言的设计中,一个 Interface 代表的是一种特性或者能力,我们把这些特性或能力一个个抽取出来,各自独立互不干扰。如果一个具体的类,想具备某些特性或者能力,就去实现这些 interface,随意组合。这是一种良好的设计原则,叫 interface segregation(接口隔离原则)

IoC 引擎

这一章节对于功能的添加并不多,更多的是类和接口的拆解,下面是拆解前后的结构图

拆解前

拆解后

其中:

  • ConfigurableBeanFactory: 维护 Bean 之间的依赖关系以及支持 Bean 处理器

  • ListableBeanFactory: 对BeanDefinition的管理

  • ConfigurableListableBeanFactory: 集成AutowireCapableBeanFactory、ListableBeanFactory 和 ConfigurableBeanFactory

  • AutowireCapableBeanFactory:由于 ConfigurableListableBeanFactory 继承了 AutowireCapableBeanFactory,

    • 所以调整之前定义由 class 改为 interface。

  • AbstractAutowireCapableBeanFactory:新增抽象类,替代原有的实现类。

  • DefaultListableBeanFactory:IoC 引擎,它继承了其他 BeanFactory 类来实现 Bean 的创建管理功能

下面是Spring中的继承体图,方便和Mini-Spring对比学习。

事件的发布与监听

  1. 抽取 ApplicationContext 接口,实现更多有关上下文的内容。

  2. 支持事件的发布与监听。

  3. 新增 AbstractApplicationContext,规范刷新上下文 refresh 方法的步骤规范,且将每一步骤进行抽象,提供默认实现类,同时支持自定义。

  4. 完成刷新之后发布事件。

之前:

之后:

最后让ClassPathXmlApplicationContext继承AbstractApplicationContext,使其集成容器应用上下文和事件发布与监听

public void refresh() throws BeansException, IllegalStateException {
    //处理 Bean 以及对 Bean 的状态一些操作
    postProcessBeanFactory(getBeanFactory());

    registerBeanPostProcessors(getBeanFactory());

    //初始化事件发布者
    initApplicationEventPublisher();

    //初始化完毕的 Bean 进行应用上下文刷新
    onRefresh();
    //注册监听者
    registerListeners();

    //完成刷新后进行自定义操作
    finishRefresh();
}
 

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值