写点什么

手写模拟 Spring 底层原理 -Bean 的创建与获取

  • 2023-03-03
    四川
  • 本文字数:1841 字

    阅读完需:约 6 分钟

手写模拟Spring底层原理-Bean的创建与获取

作者:京东物流 张鼎元

1 引言

大家好,相信大家对 Spring 的底层原理都有一定的了解,这里我们会针对 Spring 底层原理,在海量的 Spring 源代码中进行抽丝剥茧手动实现一个 Spring 简易版本,来促进我们对 Spring 架构有个更深的理解,对 Spring 的常用功能进行手写模拟实现。

2 启动 Spring

针对 Bean 的创建和获取功能,我们来进行功能的实


首先我们创建 JdApplicationContext 类做为 Spring 启动类,实现 bean 的加载和获取功能。


UserService 和 OrderService 类作为 Bean 的实现类,通过 JdApplicationContext 类中的 getBean 方法获取到前面两个类的实现。


  • App 为启动测试类

  • AppConfig 为启动配置类


注:下面的代码会顺着内容讲解逐步完成


首先创建 App 类做为入口,测试 Spring 功能。通过初始化 JdApplicationContext 类,动态加载 bean 实例。 通过 getBean 方法获取 bean 实例。



创建 JdApplicationContext 类,提供获取 Bean 实例方法,通过构造函数动态初始化 bean 实例。


3 扫描类路径并缓存 BeanDefinition 数据

在 JdApplicationContext 类初始化的时候,通过 AppConfig 配置类获取类的扫描路径,在扫描路径下,找到需要创建 Bean 的类,通过标注 Component 注解的类识别需要创建的 Bean。


通过 Component 注解识别出的类,进行封装成 BeanDefinition. 再缓存到 beanDefinitionMap 内存中。



上述的代码中,我们发现创建 BeanDefinition 类时,封装了 class 类,beanName,scope 三个主要属性。用于创建 bean 的时候,提供 class 类进行初始化和属性的注入,创建单例类或原型类提供数据依据。

4 初始化 Bean 和依赖注入

接下来,在上面的扫描操作完成后,所有待初始化的 bean 数据存储 beanDefinitionMap 中。我们只需要遍历 beanDefinitionMap 数据进行逐个初始化和属性的注入。



上述代码中,对 bean 进行初始化时候,从 beanDefinition 中获取要初始化的 class,通过反射机构进行无参初始化。


初始化完成后,再对有 Autowired 注解的属性进行依赖注入,Autowired 注解没有传递 value 值时默认取属性名称作为 beanName,通过 getBean 方法获取 bean 实例。


getBean 方法会通过 beanName,从 beanDefinitionMap 中取得 beanDefinition 数据。通过 beanDefinition 确认该 bean 为单例类原型类


如果为原型类,直接调用 createBean 方法进行 bean 初始化。


如果为单例类,首先从 singletonBeanMap 缓存中获取 bean 实例。如果未获取到,调用 createBean 方法获取 bean 实例,同时将已创建 bean 实例缓存到 singletonBeanMap 缓存中。


此时,在上述的功能中,依赖注入简易版本已实现。同时我们注意到 UserService 和 OrderService 可能会产生循环依赖的问题,在这里如何解决呢?


问题代码如下 :




上图就是循环依赖问题代码导致的异常。重复创建 bean 进入死循环。


在初始化 bean 和属性注入之间,我们可以增加二级缓存作为突破口,解决死循环问题。


userService 初始化后,需要注入 orderService,通过 getBean 方法获取,因为 orderService 没有在 singletonBeanMap 缓存中,也需要初始化并注入 userService 属性, 同时 userService 还在初始化过程中,不能缓存到 singletonBeanMap 缓存中。造成彼此循环等待属性的注入。为解决此问题,我们只需要设立初始化过程中缓存到 creatingBeanMap 中,在 userService 初始化过后,未进行属性注入前缓存到 creatingBeanMap 中,userService 需要的 orderService 属性在创建 bean 实例过程中,优先从 creatingBeanMap 缓存中得到 userService 实例,来完成 bean 实例的创建过程。orderService 完成 bean 实例创建后,userService 也相应的完成实例创建。


5 实现 InitializingBean 接口

在 createBean 过程中,我们可以对外提供初始化扩展接口 InitializingBean 接口。只要实现该接口,我们就可以针对 bean 的初始化进行扩展功能实现。 ![]


6 实现 BeanPostProcessor 接口模拟 AOP

首先创建 BeanPostProcessor 接口,作为所有 bean 实例的对外扩展接口



创建 BeanPostProcessor 接口实现类,模拟 AOP 功能,指定 userService 类进行切面。



在扫描类的时候,将已实现 BeanPostProcessor 接口类缓存到 beanPostProcessorList 中。



通过上面的扫描,beanPostProcessorList 已缓存所有的 BeanPostProcessor 实现类。在 createBean 的时候,对已创建的 bean 实例进行预处理扩展。



通过上述代码的实现效果如下:



源代码:


https://3.cn/109Aj-Zok

7 总结

在上述的讲解中,我们对 Spring 底层原理进行简单的实现,通过对类的扫描,注解标识的判断,beanDefinition 的定义和缓存。通过反射和代理进行 bean 实例的创建和扩展。相信大家也看出来在实现过程中,有很多地方需要改进,还可以继续扩展 Spring 很多其它功能。例如扩展 beanDefinition 的注册,引入 Bean 工厂,延迟加载等。

发布于: 刚刚阅读数: 3
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
手写模拟Spring底层原理-Bean的创建与获取_spring_京东科技开发者_InfoQ写作社区