有一句名言:“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决(Any problem in computer science can be solved by anther layer of indirection)” 。
这就是为啥我们从servlet说到了HttpServlet还没有进入主题SpringMVC的源码的原因,中间加了太多层。
言归正传,看上图,上一节我们说道了HttpServlet,然后下面又是一个HttpServlet的子类,HttpServletBean,并且也是一个抽象类,这就是说明,HttpServletBean也不是主角,它也是一个中间层,用来解决一个问题的。
解决什么问题?答案就是:把配置文件中的init-param配置的参数,都转换成bean的成员变量,方便使用。这一层主要的就是解析servlet配置文件中的参数,有人可能会问了:为了这个单独搞一层值当的吗,这么费劲?
如果你自己研究各种开源代码的话,可能会发现,很多前辈们的设计,就是一个洋葱,洋葱心是一个核心功能,然后每个功能都抽象出一层,一层一层的往洋葱心上贴,到最后一个完整的洋葱就出来了。
为什么这么做呢?好处是什么?这么做其实是遵循了一个原则,叫单一职责,每一个抽象层就做一件事。
我们可以回忆下,Servlet:是servlet协议的一些约定方法,是核心。GenericServlet:提供了默认的生命周期管理,HttpServlet:提供了Http协议的Servlet实现。
这样每一层只做一件事,好处是万一要做任何定制都可以非常方便。
假如一层有多个功能,而你只想定制其中一个功能,那么你不得不把所有的功能都覆盖一遍,累不累?
当然这只是个普遍的规律,肯定有不是单一职责的层,大家不要较真。其实越往洋葱的外层,类聚合的接口越多,类的功能也越多,这个时候其实不是在做单一职责的设计了,是在使用单一职责设计出来的东西;也就是说,不是单纯的在做设计了,是在使用前面设计出来的结构解决问题,实现我们的目的。因为不管你做多少设计,最终的目的就是解决我们的问题,所以洋葱的最外层就是解决问题的地方了,这些地方当然也有设计了,只是不是单一职责罢了。
继续说HttpServletBean🤣,上面说了,它的目的是解析init-param配置参数
请大家注意:第一处,这个方法是主要逻辑,但是大家不知道注意到了没有,这是重写的的父类的方法;第二处是调用了自己的初始化方法,但是这个初始化方法没有东西,留给子类实现。这是一个设计,从servlet到现在的HttpServletBean,都是子类重写父类的init方法,然后自己的init,留给自己的子类去实现。这是设计模板类的诀窍,能让子类参与到初始化中,方便子类定制自己的东西。
这个方法的主体逻辑就是将init-param的参数解析成一个map,然后将这个map中的参数注入到子类的属性中,它的子类是FrameworkServlet,也是抽象的,所以最终是注入到DispatcherServlet中。只要配置的参数名字跟属性名匹配,就可以注入。
逻辑代码没有一句一句的解析,因为我个人觉得,刚开始研究一份源代码,最重要的是先理清大体的脉络和思路,让你不至于看着看着晕头转向,迷了路。我们只要摸清楚了大体的思路和方向,然后可以仔细研究每个方法的实现。方法实现非常的复杂,调用关系乱七八糟,如果你一开始就钻进去,最后很可能会处处碰壁,灰心丧气。
另外,阅读开源代码的注释非常重要,我们可以从注释中得知作者的设计意图,使用方法,注意事项等等。
网友评论