从这一章开始我将逐步研发一个MVC框架并内嵌在我的开源项目中。由于内容会很多,所以我尽量拆分成多个章节来介绍,以展示框架研发的过程和一些细节。
上面上传的代码是整套MVC框架设计的起点,这些都是我们在spring中常见的注解类,声明好这些类后,在实际开发过程中我们就可以利用这些注解来生生成单例实例并交由spring管理了,在研发的这套框架中在能完成正常的业务开发需求之上,还简化了了很多繁琐的设计,然后还给自己的这一套注解类起了一个响亮的别名:My*系列。
一般的我们都是按照这样的结构设计项目的包目录的:
只要有统一的父节点,包目录分散的都不要紧,无非就是在扫描包的时候消耗一点性能,多扫一些无用的类罢了。设计好包目录结构后,再来配置一下扫描规则,如下图所示:
四个类型的包扫描配置,用数组的形式配置,支持你分散设计包的目录结构,没有任何限制,配置也很简单。
下面我就按照我对spring的理解来分析一下整个spring项目的网状交叉依赖关系是怎么启动和注入完成的。因为我的分析是直接给出的结论并没有叙述思考问题的角度及得出结论的过程,所以会让人看起来觉得很简单,但是实际实现起来细枝末节的问题很多。
dao层扫描
首先根据我们多年开发的经验知道,dao层代码一般都是只定义接口类和方法,不会有交叉bean的引入的,所以第一步我们要先扫描dao层下的所有接口类。这里唯一和别的类不同的区别是dao的实例是动态代理实例,就是最终代码在调用接口方法时会监听该方法并由框架处理接口的实现。
service层扫描
把dao层下的所有接口扫描完,装载到内存中,按照完整类名作为key,实例对象作为value存入到dao的map缓存中,然后开始扫描servcie层下的包。由于servcie层包确认是不会引入controller层的bean的,所以处理起来会相对简单一点。
考虑到service层会依赖互相注入service层bean还会注入一些帮助类的bean,所以就得扫描自身类的同时继续通过反射把类的成员变量中的所有bean也扫描出来。拿到类的成员变量后根据类名去对应类型的缓存中查询下是否当前类的bean已经存在,然后处理好创建和赋值操作。当类的成员变量也是bean且没有在缓存中存在实例,则继续对成员变量做扫描,直至扫描到最后一个bean为止。
controller层扫描
controller扫描还需要校验url不能重复,url上的注解不能为空等,也就这些和别的扫描有些区别。然后还需要把url和controller的方法映射存储起来,因为在做反射调用的时候是根据url来寻找controller类实例来执行对应方法的。
component层扫描
这一层其实嵌套在service层内实现,也可以嵌套在controller内,在扫描上面的包的过程中扫描到component就已经完成了其bean的生成和注入了。
请求调度
整个所有的bean扫描完成后,就可以交由mvc的servlet调度控制类来做调度了。写一个servlet类拦截所有servlet请求,交由mvc控制其任务的调度、务的控制、参数包装、结果返回以及异常处理。这样整个mvc的部分就算开发完了。
特别的,dao层的使用一般我们还需要借助xml文件来存储sql文件,这是一个很方便书写、管理和移植拓展的解决方案。所以在mvc框架开发的最后一步就是把前期手写的字符串SQL替换为xml的mapper文件来处理。而这个我也会单独出章节来讲。在java最新的版本中已经支持字符串模板的写法了,它支持一段字符串的直接所见即所得的写法,非常适合写html和sql文件,到时候我在看下能不能走这条路来支持sql这一块的解析。