手把手带你写一个 MiniSpring
郭屹
前 Sun Microsystems Java 研发工程师,开源软件 MiniSpring、MiniTomcat 开发者
6170 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 26 讲
手把手带你写一个 MiniSpring
15
15
1.0x
00:00/00:00
登录|注册

09|分解Dispatcher:如何把专门的事情交给专门的部件去做?

你好,我是郭屹。今天我们继续手写 MiniSpring。
经过上节课的工作,我们已经实现了 IoC 与 MVC 的结合,还定义了 Dispatcher 与 WebApplicationContext 两个相对独立又互相关联的结构。
这节课我们计划在已有的 ApplicationConfigWebApplicationContext 和 DispatcherServlet 基础上,把功能做进一步地分解,让 Dispatcher 只负责解析 request 请求,用 Context 专门用来管理各个 Bean。

两级 ApplicationContext

按照通行的 Web 分层体系,一个程序它在结构上会有 Controller 和 Service 两层。在我们的程序中,Controller 由 DispatcherServlet 负责启动,Service 由 Listener 负责启动。我们计划把这两部分所对应的容器进行进一步地切割,拆分为 XmlWebApplicationContext 和 AnnotationConfigWebApplicationContext。
首先在 DispatcherServlet 这个类里,增加一个对 WebApplicationContext 的引用,命名为 parentApplicationContext。这样,当前这个类里就有了两个对 WebApplicationContext 的引用。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何将Dispatcher进一步分解,让其专注于解析request请求,而将Bean的管理交给专门的Context来处理。作者首先提到了在程序结构上会有Controller和Service两层,计划将这两部分对应的容器进一步切割为XmlWebApplicationContext和AnnotationConfigWebApplicationContext。在DispatcherServlet类中增加了对WebApplicationContext的引用,命名为parentApplicationContext,用于区分Listener启动的上下文和DispatcherServlet启动的上下文。接着,作者详细介绍了对AnnotationConfigWebApplicationContext的改造,扩充了原有的构造方法,实现了对父上下文和servletContext的引用,并对DefaultListableBeanFactory进行了改造。同时,还对XmlWebApplicationContext进行了改造,实现了WebApplicationContext接口。最后,作者指出在Web环境下的两个ApplicationContext都构建完毕,WebApplicationContext持有对parentApplicationContext的单向引用。文章通过详细的代码解析和讲解,帮助读者了解了如何将Dispatcher进一步分解,实现了专门的事情交给专门的部件去做的目标。文章还介绍了如何通过新增RequestMappingHandlerMapping与RequestMappingHandlerAdapter来分别处理URL映射关系和映射后的处理过程,从而将DispatcherServlet的功能进行了分治,有利于今后的扩展。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 MiniSpring》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(10)

  • 最新
  • 精选
  • 马儿
    总结一下: 1. Listener初始化的时候将交给Ioc管理的Bean初始化 2.Servlet初始化的时候将controller相关的bean初始化 这两步初始化将bean的管理从DispatcherServlet剥离交给了第一章创建的Ioc容器 3.将具体的url和对象+方法的管理从Servlet交给HandlerMapping来处理 4.将具体的方法执行剥离到HandlerAdapter 这两步将DispatcherServlet变得更抽象了,利用serviece方法可以同时处理不同类型的请求 一点建议: 1. DispatcherServlet中的controller相关bean的初始化已经交给AnnotationConfigWebApplicationContext管理了,它的init方法不用在调用initController了

    作者回复: 赞你一个

    2023-04-01归属地:四川
    8
  • lmnsds
    在github代码的geek_mvc3分支找了半天 这节课的 DispatcherServlet,原来不是在web包下改的原有类,而是在web.servlet包下新增了个DispatcherServlet!浪费了好多时间!给后来人提个醒吧。

    作者回复: 多谢提醒

    2023-05-15归属地:北京
    2
  • 风轻扬
    思考题:我的想法是模仿SpringMVC,在RequestMapping注解上增加一个HttpMethod的属性(当前方法允许的请求方式)。在解析RequestMapping注解的时候改动一下,拿到RequestMapping注解上的HttpMethod,将其放到HandlerMethod中,然后将HandlerMathod对象放进MappingRegistry的一个map中,key:path,value:HandlerMethod。用户发起请求时,doDispatch方法中,获取到HttpServletRequest对象中的请求方式和MappingRegistry中存储的HandlerMethod上的请求方式进行比较,如果符合就可以访问,否则就报出方法类型不匹配 另外,有两个问题请教一下老师。 1、问题一: DefaultListableBeanFactory beanFactory; DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); this.beanFactory = bf; 我看老师在很多地方都是这样写的,为啥不直接给成员变量赋值呢?this.beanFactory = = new DefaultListableBeanFactory(); 2、问题2 为什么Spring要搞出两个容器来呢? 我从StackOverFLow上搜了一下相关解释:https://stackoverflow.com/questions/18578143/about-multiple-containers-in-spring-framework 看上面的解释是: 这样分开更清晰,Dispatcher驱动的子容器专门用来处理controller组件,ContextLoaderListener驱动的父容器专门用来处理业务逻辑组件以及持久化组件。 除了这个原因,Spring搞2个容器还有其他原因吗?

    作者回复: 思考题后面有再回首章节里面给出了参考,你可以到时候再思考一下。 Q1,习惯问题吧,Spring框架本身也是经常这样写。有一个潜在原因,但是我不是很确定,就是方法中尽量使用local变量,只在尽量少的场合使用实例变量和方法参数,这样提高性能减少开销。编译器优化是这么说的,但是我也不确定一定就是这样。另外,对源码,我还是建议最后去读Spring框架的源代码,那是世界顶级程序员的力作(推广到别的也是一样的,Apache Tomcat,JDK,Apache Dubbo)。我自己也是在跟着他们的时候一旁偷学了几招,MiniSpring的目的是剖析Spring框架内部结构,作为一个简明地图引导大家理解Spring。 Q2,就是这样解释的。Spring体系中IoC是核心层,MVC只是外周的部分,理论上是可以不启用的可选部件。软件是一层层堆积的,层次感要慢慢培养。

    2023-04-08归属地:北京
    2
    2
  • 赵欣
    有几个文件跟原来版本相比也有些变化了,大家注意下,一个是AbstractBeanFactory.java一个是DefaultListableBeanFactory.java文件。

    作者回复: 赞你

    2024-03-15归属地:广东
  • 睿智的仓鼠
    不可多得的好课,跟到现在学到很多

    作者回复: 感谢!

    2023-06-07归属地:湖北
  • 梦某人
    首先以个人理解回答课后题,目前的请求并不分 get 或者 post,主要是以请求的路径进行区分,如果想要处理 post 请求, 需要构建新的 HandlerAdapter,对 Request 中的 post body 内容进行额外的解析和处理,然后操作方法。当然可能还需要构建 HandlerMapping 来处理请求路径,但是个人没想到什么 get 和 post 区别很大的地方。 第二和第三点是个人跟写的时候遇到的一些问题,给其他同学一点参考。 第二个, ``` // getBeanDefinitionNames 方法中 return (String[]) this.beanNames.toArray(); //替换成 return this.beanNames.toArray(new String[0]); ``` 可以减少一些类型转换异常,特别是当 ArrayList 里面只有一个 String 元素的时候。 第三个,BeanDefinition 中的部分 get 方法应该增加运算符 防止返回 Null 而不是 empty,导致空指针异常。例如: ``` public ArgumentValues getArgumentValues() { return argumentValues == null? new ArgumentValues(): argumentValues; } ``` 第四点是课程个人理解了:两级的 WebApplicationContext 第一级在 Listener 的时候加载,加载了 beans.xml (或者 Application.xml ) 中的 bean, 然后作为 第二级 AnnotationConfigWebApplicationContext 的父级, 第二级别通过 mvc.xml 提供的扫包路径进行扫包加载 bean,同时注册带有注解的方法。 当路由请求来的时候,先从第二级的 WebApplicationContext 获取 bean 和其方法进行处理,所以这个两级在最后的时候以 Controller 和 Service 来进行讲解,不是真的 Controller 和 Service, 而是说 第二级处理事物的触发逻辑比第一级更早,加载的逻辑则比他更晚,就好像 请求先到 Controller 后到 Service 一样。 最后的最后,,,看着老师文稿给的代码来吵,已经是和 GitHub 中的代码差别越来越大了, Debug 起来更加费时,但是好处是理解加深了。

    作者回复: 写得很好很好。我觉得,不管通过什么方式,最后能达到理解了Spring框架的目的就好了,我的建议还是读文稿,听我讲,手工练习,自己动手过一遍就是不一样的,线下班学员还要做扩展练习,加上更多功能和鲁棒性。源代码方面,MiniSpring主要是便于教学时理解Spring框架的结构,代码最后要学习Spring框架本身的源代码。

    2023-04-05归属地:河北
  • C.
    出去玩了两天,今天把这章也结束掉了。代码运行一切正常。

    作者回复: 赞

    2023-04-03归属地:江苏
  • Geek_320730
    1. 课后题:重写service后不是Get 和Post都能处理吗? 2.扫描的包里有接口,接口应该是不能实例化的,我过滤了下接口类型才能启动起来,对比了下老师的代码,好像并没有处理。 3.尝试了下在HelloWorldBean里注入parentApplicationContext中创建的Bean,发现了个小问题,AbstractBeanFactory#getBean方法中如果获取不到BeanDefinition 应该返回个null,而不是抛出异常,否则不会去父类查找。对构造器注入参数和set注入参数增加null校验

    作者回复: 你的思考和练习很好有深度,用心了。MiniSpring是为了学习构建的,不是工业级的。线下听课的几波学生,要对照代码进行扩展练习,自己增加功能特性增加鲁棒性。

    2023-04-02归属地:北京
    2
  • peter
    请教两个问题: DispatcherServlet 这个类里,有两个WebApplicationContext对象:private WebApplicationContext webApplicationContext; private WebApplicationContext parentApplicationContext; 请问,这两个对象是同一个对象吗?? Q2:文中的controller和service是业务层的吗? 文中有这样的描述:“按照通行的 Web 分层体系,一个程序它在结构上会有 Controller 和 Service 两层。在我们的程序中,Controller 由 DispatcherServlet 负责启动,Service 由 Listener 负责启动。” 程序员写业务代码的时候,会按照controller、service、dao来写。 请问,文中的controller和service是业务层的controller、service吗?(即程序员写的controller、service)

    作者回复: 不是同一个,是两级context. controller和service是两层,并不特别规定是谁。我这里主要想描述的是dispatcherservlet中的控制逻辑与ioc容器中管理的bean的业务逻辑是前后两层的关系。

    2023-04-01归属地:北京
    2
  • Geek_149cde
    不明白 AnnotationConfigWebApplicationContext 文件里 loadBeanDefinitions 加载的时候不应该把 AService 接口也加载进去了吗?创建 Bean 的时候不是就报错了
    2023-07-03归属地:山西
收起评论
显示
设置
留言
10
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部