一、目标

在上一章节我们初步依照 Spring Bean 容器的概念,实现了一个粗糙版本的代码实现。那么本章节我们需要结合已实现的 Spring Bean 容器进行功能完善,实现 Bean 容器关于 Bean 对象的注册和获取。这一次我们把 Bean 的创建交给容器,而不是我们在调用时候传递一个实例化好的 Bean 对象,另外还需要考虑单例对象,在对象的二次获取时是可以从内存中获取对象的。此外不仅要实现功能还需要完善基础容器框架的类结构体,否则将来就很难扩容进去其他的功能了。

二、设计

鉴于本章节的案例目标,我们需要将 Spring Bean 容器完善起来,首先非常重要的一点是在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息注册到 Spring 容器中。那么就需要修改 BeanDefinition 中的属性 Object 为 Class,接下来在需要做的就是在获取 Bean 对象时需要处理 Bean 对象的实例化操作以及判断当前单例对象在容器中是否已经缓存起来了。整体设计如下

以下内容其实就是围绕着Spring的容器需要有单例,缓存,实例化,异常这些功能展开的

  1. 解析
  • 首先我们需要定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取方法getBean(String name),之后这个 Bean 工厂接口由抽象类AbstractBeanFactory 实现。这样使用模板模式的设计方式,可以统一收口通用核心方法的调用逻辑和标准定义,也就很好的控制了后续的实现者不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体方法的逻辑实现即可。
  • 那么在继承抽象类 AbstractBeanFactory 后的AbstractAutowireCapableBeanFactory 就可以实现相应的抽象方法了,因为AbstractAutowireCapableBeanFactory 本身也是一个抽象类,所以它只会实现属于自己的抽象方法,其他抽象方法由继承 AbstractAutowireCapableBeanFactory 的类实现。这里就体现了类实现过程中的各司其职,你只需要关心属于你的内容,不是你的内容,不要参与。这一部分内容我们会在代码里有具体的体现
  • 另外这里还有块非常重要的知识点,就是关于单例 SingletonBeanRegistry 的接口定义实现,而 DefaultSingletonBeanRegistry 对接口实现后,会被抽象类AbstractBeanFactory 继承。现在 AbstractBeanFactory 就是一个非常完整且强大的抽象类了,也能非常好的体现出它对模板模式的抽象定义。接下来我们就带着这些设计层面的思考,去看代码的具体实现结果
  • 类依赖图

基本都是接口做出约定,抽象类给出场景,子类具体实现,然后接口调用。

这一章节关于 Spring Bean 容器的功能实现与 Spring 源码中还有不少的差距,但以目前实现结果的类关系图来看,其实已经具备了一定的设计复杂性,这些复杂的类关系设计在各个接口定义和实现以及在抽象类继承中都有所体现,例如:

  • BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 方法
  • AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean的注册功能
  • AbstractBeanFactory 中又定义了两个抽象方法: getBeanDefinition(StringbeanName) 、 createBean(String beanName, BeanDefinition beanDefinition) ,而这两个抽象方法分别由DefaultListableBeanFactory 、AbstractAutowireCapableBeanFactory 实现。
  • 最终 DefaultListableBeanFactory 还会继承抽象类AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的 createBean 方法了。


通过最终的DefaultListableBeanfatory new出的对象beanFactory已经具备了很多功能。
怎么设计还是体现一个最基本的原则:类实现过程中的各司其职,你只需要关心属于你的内容,不是你的内容,不要参与。

三、代码实现

  • 工程结构
  • BeanDefinition
//在 Bean 定义类中已经把上一章节中的 Object bean 替换为 Class ,这样就可以把Bean 的实例化操作放到容器中处理了。
//如果你有仔细阅读过上一章并做了相应的测试,那么你会发现 Bean 的实例化操作是放在初始化调用阶段传递给BeanDefinition 构造函数的。
public class BeanDefinition {private Class beanClass;public BeanDefinition(Class beanClass) {this.beanClass = beanClass;}public Class getBeanClass() {return beanClass;}public void setBeanClass(Class beanClass) {this.beanClass = beanClass;}
}
  • SingletonBeanRegistry
 // 单例注册表//这个类比较简单主要是定义了一个获取单例对象的接口。
public interface SingletonBeanRegistry {Object getSingleton(String beanName);
}
  • SingletonBeanRegistry
//在 DefaultSingletonBeanRegistry 中主要实现 getSingleton 方法,同时实现了一个受保护的 addSingleton 方法,
//这个方法可以被继承此类的其他类调用。包括:AbstractBeanFactory 以及继承的 DefaultListableBeanFactory 调用。
public class cimplements SingletonBeanRegistry {private Map<String, Object> singletonObjects = new HashMap<>();@Overridepublic Object getSingleton(String beanName) {return singletonObjects.get(beanName);}protected void addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);}
}
  • 抽象类定义模板方法 (AbstarctBeanFactory)
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Overridepublic Object getBean(String name) throws BeansException {Object bean = getSingleton(name);if (bean != null) {return bean;}BeanDefinition beanDefinition = getBeanDefinition(name);return createBean(name, beanDefinition);}protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}
  • AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry ,也就具备了使用单例注册类方法
  • 接下来很重要的一点是关于接口 BeanFactory 的实现,在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean的定义做相应 Bean 实例化操作。那么 getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
  • 后续继承抽象类 Abstrac tBeanFactory 的类有两个,包括:AbstractAutowireCapableBeanFactory 、 DefaultListableBeanFactory ,这两个类分别做了相应的实现处理,接着往下看。
  • AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {Object bean = null;try {bean = beanDefinition.getBeanClass().newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new BeansException("Instantiation of bean failed", e);}addSingleton(beanName, bean);return bean;}
}
  • 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作newInstance ,其实这块会埋下一个坑,有构造函数入参的对象怎么处理 ?
  • 在处理完 Bean 对象的实例化后,直接调用 addSingleton 方法存放到单例对象的缓存中去。
  • 核心类实现 (DefaultListableBeanFactory)
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName, beanDefinition);}@Overridepublic BeanDefinition getBeanDefinition(String beanName) throws BeansException {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) throw new BeansException("No bean named '" + beanName + "' is defined");return beanDefinition;}}
  • DefaultListableBeanFactory 在 Spring 源码中也是一个非常核心的类,在我们目前的实现中也是逐步贴近于源码,与源码类名保持一致。
  • DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的功能实现。 所以有时候你会看到一些类的强转,调用某些方法,也是因为你强转的类实现接口或继承了某些类。
  • 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法,当然你还会看到一个 getBeanDefinition 的实现,这个方法我们文中提到过它是抽象类 AbstractBeanFactory 中定义的抽象方法。 现在注册 Bean 定义与获取 Bean 定义。就可以同时使用了,是不感觉这个套路还蛮深的。接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里

四、测试

  • 事先准备
public class UserService {public void queryUserInfo(){System.out.println("查询用户信息");}
}
  • 测试
@Test
public void test_BeanFactory(){// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2.注册 beanBeanDefinition beanDefinition = new BeanDefinition(UserService.class);beanFactory.registerBeanDefinition("userService", beanDefinition);// 3.第一次获取 beanUserService userService = (UserService) beanFactory.getBean("userService");userService.queryUserInfo();// 4.第二次获取 bean from SingletonUserService userService_singleton = (UserService) beanFactory.getBean("userService");userService_singleton.queryUserInfo();
}
  • 在此次的单元测试中除了包括;Bean 工厂、注册 Bean、获取 Bean,三个步骤,还额外增加了一次对象的获取和调用。这里主要测试验证单例对象的是否正确的存放到了缓存中。
  • 此外与上一章节测试过程中不同的是,我们把 UserService.class 传递给了 BeanDefinition 而不是像上一章节那样直接 new UserService() 操作。]

五、总结

  1. 这一章已经开始复杂,各种接口抽象类实现类开始出现,牢牢记住:基本都是接口做出约定,抽象类给出场景,子类具体实现,然后接口调用。
  2. 在 Spring Bean 容器的实现类中要重点关注类之间的职责和关系,几乎所有的程序功能设计都离不开接口、抽象类、实现、继承,而这些不同特性类的使用就可以非常好的隔离开类的功能职责和作用范围。而这样的知识点也是在学习手写Spring Bean 容器框架过程非常重要的知识。

手写简版spring --2--实现Bean的定义、注册、获取相关推荐

  1. 手写简版spring --1--创建简单的Bean容器

    一.声明 这个系列是我自己的学习笔记,为了在学习的过程中巩固知识而记录的,好强迫自己用心拜读,而不是进收藏夹.本系列都是基于小缚哥的文章和代码的,想要深入了解,请移步小缚哥博客 二.spring-Be ...

  2. 手写简版spring --10--容器事件和事件监听器

    一.降低耦合 解耦场景在互联网开发的设计中使用的也是非常频繁,如:这里需要一个注册完成事件推送消息.用户下单我会发送一个MQ.收到我的支付消息就可以发货了等等,都是依靠事件订阅和发布以及MQ消息这样的 ...

  3. 手写简版spring --9--对象作用域和FactoryBean

    一.目标 交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例的吗,没有可能是原型模式吗?在集合 Spring 框架下,我们使用的 ...

  4. 手写简版spring --8--Aware感知容器对象Aware感知容器对象

    一.目标 目前已实现的 Spring 框架,在 Bean 操作上能提供出的能力,包括:Bean 对象的定义和注册,以及在操作 Bean 对象过程中执行的,BeanFactoryPostProcesso ...

  5. 手写简版spring --7--初始化方法和销毁方法

    一.目标 当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力.就像我们在上一章节已经给类对象添加了修改注册Bean定义未实例化前的属性信息修改和实 ...

  6. 手写简版spring --6--应用上下文(BeanPostProcessor 和 BeanFactoryPostProcessor)

    一.目标 如果你在自己的实际工作中开发过基于 Spring 的技术组件,或者学习过关于SpringBoot 中间件设计和开发等内容.那么你一定会继承或者实现了 Spring对外暴露的类或接口,在接口的 ...

  7. 手写简版spring --5--资源加载器解析文件注册对象

    一.目标 在完成 Spring 的框架雏形后,现在我们可以通过单元测试进行手动操作Bean对象的定义.注册和属性填充,以及最终获取对象调用方法.但这里会有一个问题,就是如果实际使用这个 Spring ...

  8. 手写简版spring --4--注入属性和依赖对象

    一.目标 首先我们回顾下这几章节都完成了什么,包括:实现一个容器.定义和注册Bean.实例化Bean,按照是否包含构造函数实现不同的实例化策略,那么在创建对象实例化这我们还缺少什么?其实还缺少一个关于 ...

  9. 手写简版spring --3--对象实例化策略

    一.目标 这一章节的目标主要是为了解决上一章节我们埋下的坑,那是什么坑呢?其实就是一个关于 Bean 对象在含有构造函数进行实例化的坑.在上一章节我们扩充了 Bean 容器的功能,把实例化对象交给容器 ...

最新文章

  1. 简单介绍.Net性能测试框架Crank的使用方法
  2. 安卓AsyncTack详解
  3. JAX-RS和JSON-P集成
  4. Maven的单元测试插件maven-surefire-plugin详解
  5. 百度UEditor编辑器使用(二)
  6. 月老在线牵盲盒/交友盲盒公众号版本1.04支持交友同城
  7. 成立出版社的条件_厦门大学出版社招聘启事
  8. 循环单链表 python_循环单链表报错
  9. ios 短信验证码自动填充时总是被复制两遍
  10. Flutter基础—常用控件之容器
  11. 怎样把任意exe程序注册成windows系统服务(手动注册服务)
  12. java程序员集合框架面试题_常见的Java集合框架面试题目及回答
  13. RPG游戏开发基础教程
  14. php ping 域名,怎么利用PHP去ping一个地址_PHP
  15. sql中将字符串数字转换成数字
  16. 卓训教育:双减政策下,家长如何缓解教育焦虑?
  17. 数据结构与算法10:图与图搜索
  18. 弥散磁共振影像处理的黎曼 芬斯勒几何方法研究
  19. oracle数据库所在主机内存耗尽
  20. XP下硬盘安装fedora linux操作系统

热门文章

  1. 一个NPOI导出到excel文件的范例记录
  2. 阿里云-AliRepo
  3. LCA+差分【p4427】[BJOI2018]求和
  4. DirectX11 初探XMVECOTRXMMATRIX
  5. 算法/数据工程师必备技能
  6. 【USACO】奶牛抗议 树状数组+dp
  7. SQL增删改查,基础
  8. ubuntu 中 iptables 和 ufw 的关系
  9. Javascript 面向对象编程初探(一)--- 封装
  10. Android深度探索第四章感想