春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面

熬了半个多月的时间,每天坐在电脑面前十几个小时,从Spring官网拉下来了spring-framework这个项目的源码,边看源码,边从网上找资料,整个项目中使用的设计模式也是非常的经典,如工厂模式:BeanFactory,代理模式:AopProxy,单例模式:ApplicationContext,装饰器模式:BeanWrapper,委派模式:DispatcherServlet,策略模式:HandlerMapping,适配器模式:HandlerAdapter, 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

参考完人家的源码,我又从0到1,把这个应用层框架给写了一遍,目录结构如下图。最后又基于springboot的约定大于配置的封装,把配置文件全部给剔除了,全部使用注解即可启动服务,大概数了一下,一共有五十多个类吧。下面阐述一下该项目的核心流程,顺便附带项目在github和码云的地址。 github地址: https://github.com/985391294/spring-framework-tqz
码云地址: https://gitee.com/tianqingzhao/spring-framework-tqz
春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片
春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

1.当mian方法启动的时候,初始化tomcat的配置 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

2.tomcat的初始化工作 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

3. 在PropertiesConfig的构造方法里面加载了配置文件,如果有的话就去读取端口号和项目,如果没有就返回一个默认值 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

4.tomcat的配置完成之后,向该容器中注册一个DispatcherServlet,这个servlet继承了HttpServlet,然后实现了里面的三个方法,init()、doGet()、doPost(), 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

5.检查启动类是否有@SpringBootApplication注解,如果有的话就初始化spring上下文环境以及注册springmvc的三大核心组件。 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

6.进入到ApplicationContex的构造方法,然后调用refresh()方法 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

7进入到spring的初始化工作,首先把当前项目所有的类给放到一个list中 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

8.然后把这些类先解析为BeanDefinition,再存到一个list中。解析的时候回去检查这个类是不是接口,如果是接口就不要,是抽象类也不要,没有添加Controller注解或者Service也不要,符合规则的话,然后再判断这个类是不是懒加载,懒加载的类在spring上下文初始化的时候是不是进行实例化的。spring源码中还会判断这个类是原型还是单例,以及作用域、类的描述等一些列属性。 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

9.再把这些BeanDefinition放到一个BeanDefinitionMap中,存两次的目的是既可以根据beanName获取到,也可以根据类的类型获取到 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

10.从BeanDefinitionMap中拿出来所有的bean,判断一下这个bean是不是懒加载,懒加载就不会进行初始化,只有在用到的时候才会初始化。 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

11.在这个地方是整个spring非常重要的地方,AOP和DI都是在这个地方完成的 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

12.AOP是在初始化之后,属性注入之前完成的,当对象创建出来之后,会去看一下这个对象里面方法是否符合切面表达式的规则,如果符合就进行代理。 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

在目标类创建出来之后,去解析当前目标类

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

如果符合规则的话,就把当前切面的通知给配置为一个执行器链,依次是前置通知、后置通知、异常通知、最终返回通知,异常通知和最终返回通知只能共存一个, spring中海油一个环绕通知,这里省略。。。。。。

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

13.AOP的具体逻辑如下,通过jdk的动态代理创建出来代理对象,然后让该类实现HandlerInvocation接口,实现invoke方法,当执行目标方法的时候,会调用invoke方法,执行调用链。春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片
通过proceed()方法进入到执行链

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

14.执行器链依次执行几大通知方法 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

这里以before通知为例,如果是前置通知就在执行下一个拦截器之前调用,如果是后置通知就在下一个拦截器执行完之后调用,异常通知就在catch代码块里面执行,需要注意的是如果有异常通知,被代理的方法不能捕捉异常,只能向外面抛,因为aop的异常拦截是在有异常的时候才会执行,如果给捕捉了,对于整个调用链来说相当于是没有异常的,最终通知就放在finally代码块里面,不管有没有异常都会执行,

【春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面】春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

通过这里执行通知方法的调用,至此整个aop完成了。

春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

15.spring的上下文环境初始化完成之后就该完成mvc的三个核心组件注册了,我们只关心HandlerMapping、HandlerAdapter、ViewResovler 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

16.这里又是从容器中拿到所有的bean,然后判断当前bean有没有添加Contrller注解,如果添加了Controller注解,就去继续解析这个bean,把类上面的url和方法上面的url拼接到一起然后用正则编译一下,然后封装给HandlerMapping,再把这个HandlerMapping放到一个list中 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

17.HandelrAdapter又是跟HandlerMapping保持一一对应的关系,目的就是处理每个方法里面的参数,做一个参数动态匹配 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

18.ViewResolver,先去看一下配置文件里面有没有配置模板引擎,如果没有配置就是用默认的templates 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

19.用户发送请求到前端控制器 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

20.根据请求的url拿到handlerMapping对象,拿不到就返回404,拿到的话再根据handlerMapping找到对应的hanlderAdapter,然后去处理参数,返回一个ModelAndView对象 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

21.根据ViewResolver解析当前ModelAndView,返回一个View对象,view对象再去处理当前的model里面的数据以及页面名称,通过response输出到浏览器。 春天来了,继续手写自己的Spring,从0到1,从IOC到DI、AOP、MVC四个阶段,项目已经传到了github和码云上面
文章图片

到这里整个spring-frameworke就完成了,不知道有没有忘记介绍什么内容。

    推荐阅读