转载

手写Spring Framework新实践

前言

Spring的好处我这里不说,地球人都知道。相信有许多小伙伴,都阅读过Spring源码,毕竟它是我们项目中使用最多的框架,没有之一。不知道大家什么感觉,起初我阅读的过程中一行一行的读,结果当然是被Spring的工厂、委派、策略、模板的各种模式整的不知所措。

后来带着目的性去阅读,比如我先定个小目标,“Spring如何获取class文件的?”这一个问题入手,开始阅读。用gradle编译好源码后,从AnnotationConfigApplicationContext的构造方法开始,一阵凌乱的Debug后,得到最终“一目了然”的方法(如下图所示)。看到这个方法后,先不用看方法体,只看入参,大概就明白了Spring是怎么做的,拿到绝对路径,再根据正则表达式,获取所有本路径下的 class 文件。然而到这个“干实事儿”的方法经历了20多次调用过程,我也是Debug了十分钟才拿到最核心的方法。

手写Spring Framework新实践

手写Spring Framework新实践

然后我开始想,既然对于新手,读起来如此费力,要不干脆写出一个简化版,首先抛开复杂的设计模式,直接实现它最核心的业务;如果能理解并能写出核心业务,再回头读源码,看其对核心业务如何封装加特技,那么到时候肯定就不慌了。

说干就干,凭着自己也对源码的理解外加参考资料,写出来了一版:github地址:git@github.com:HenryWangXin/mySpringFrameV2.git 在我写的过程中,包括类名、方法名尽量靠近源码的命名规则,只是做了简化只实现其核心功能,虽然与源码相差甚远,但是麻雀虽小五脏俱全。目前功能上实现了springMVC 和 IOC以及DI,AOP正在编写中。

由于篇幅有限这篇只介绍IOC的实现和DI。

项目目录与演示效果

    0 1

    项目目录介绍

手写Spring Framework新实践

    0 2

    演示效果

输入:http://localhost:8082/wx/query?name=wangxin;就会得到下面的结果:

手写Spring Framework新实践

    项目启动

    01 【application.properties】配置加载类路径

手写Spring Framework新实践

    0 2

    【web.xml】容器项目起始文件

手写Spring Framework新实践

    03

    通过maven的jetty插件启动

手写Spring Framework新实践

    MVC模块简介

    01

    MVC如何出初始化

容器启动读取Web.xml时会初始化定义的WXDispatcherServlet类:

手写Spring Framework新实践

首先会调用WXDispatcherServlet的init方法;

手写Spring Framework新实践

Init方法中做了两件事: 1、初始化IOC容器; 2、IOC容器初始化完成后,初始MVC的9大组件。

也就是说MVC架构的初始化,是建立在IOC容器初始化完成之后的。

手写Spring Framework新实践

    02

    MVC模块UML时序图

由于本文着重讲初始化IOC容器,所以这里只画出MVC实现的时序图:

手写Spring Framework新实践

    03

    MVC架构流程简单介绍

1.用户发送请求至前端控制器DispatcherServlet。

2.DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3.处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4.DispatcherServlet通过HandlerAdapter处理器适配器调用处理器。

5.执行处理器(Controller,也叫后端控制器)。

6.Controller执行完成返回ModelAndView。

7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9.ViewReslover解析后返回具体View。

10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

11.DispatcherServlet响应用户。

    IOC容器初始化

上文讲到web容器启动时,初始化了WXApplicationContext,这个类是spring-framework的核心类,没有之一;通过refresh方法和getbean方法,完成了IOC、DI、AOP的操作与衔接。这个类的singletonObjects属性是map类型的,它保存了Spring帮我们整理好的,既可以帮我们干事儿的代理,又有实现了生命周期监听事件Bean;也就是大家俗称的“Spring容器”说法的具体实现;如果把它讲清讲透,大家都理解了,这篇文章的目的也就达到了。首先我们讲个重要概念BeanDefinition。

    01

    WxBeanDefinition介绍

BeanDefinition主要用于保存Bean相关的配置信息,Spring初始化实例不同于正常的初始化流程,如图所示:

手写Spring Framework新实践

    02

    WXApplicationContext属性介绍

手写Spring Framework新实践

缓存变量说明:

private final Map<String, Object> singletonObjects   //一级缓存

private final Map<String, ObjectFactory> singletonFactories//二级缓存

private final Map<String, Object> earlySingletonObjects //三级缓存

    03

    Refresh【初始化方法】

    手写Spring Framework新实践

    3.1 WxBeanDefinitionReader【扫描class,初始化Beandefinition】

    3.1.1 WxBeanDefinitionReader 构造方法里进行包扫描

包扫描这里用到了一个简单的递归。

手写Spring Framework新实践

手写Spring Framework新实践

3.1.2 loadBeanDefinitions 【通过扫描到的包路径生成 BeanDefinition list

手写Spring Framework新实践

    3.2 doRegisterBeanDefinition【BeanDefinition List 转为 BeanDefinitionMap】

这么做是为了后面的getBean方法提供方便。

手写Spring Framework新实践

    3.3 finishBeanFacotyInit【遍历BeandefinitionMap用 getBean方法】

遍历每个Beandefinition都调用一次 getBean方法。

手写Spring Framework新实践

3.3.1 getbean 方法完成了实例初始化, DI AOP

手写Spring Framework新实践

3.3.1.1 instantiateBean 【根据条件实例化一个 instance

手写Spring Framework新实践

3.3.1.2 populateBean 如何进行 DI 注入

手写Spring Framework新实践

    04

    IOC容器初始化UML时序图

手写Spring Framework新实践

    05

    循环依赖问题如何解决

手写Spring Framework新实践

    06

    完成IOC与DI

当通过finishBeanFacotyInit方法,循环把每个Beandefinition循环执行getbean时,我们的singletonObjects  也完成了最终的初始化,到此为止IOC与DI完成。

    总结

手写springframwork,其实没有想象的那么难,我相信通过我的源码,和这边文章的介绍,小伙伴们都能手写出来,对自己技术和自信心都有很高的提升,当知道了核心代码再回过头看Spring源码,一定会轻松许多。

原文  http://mp.weixin.qq.com/s?__biz=MzUyMDAxMjQ3Ng==&mid=2247492209&idx=1&sn=c07b8e6db845213af4d9bdd788a5ab4e
正文到此结束
Loading...