实现Java Web框架的一种简单思路
在 Java EE 领域,Spring 全家桶基本处于垄断地位。作为一个编程人员,用框架越久,就离底层越远(虽然我们离计算机底层已经十万八千里远了)。于是萌生了自己写一个 Java Web 框架的想法,花了些业务时间,写出来一个低仿低配版 Spring 。这篇文章从思路层面做一下总结。
概述
抛却框架,使用 java 去实现一个 Web 项目,核心是 Servlet
。一个最简单的 Web 项目,可以直接继承HttpServlet
,重写service
之类的方法,并使用@WebServlet
的注解指明urlPattern
即可。
简单的去看一个 web 项目,就是拿到request
,根据request
的内容,比如method
和path
,去查找并执行指定的业务逻辑,生成响应后,发送response
。
在spring mvc中,使用@Controller
注解去标明这是一个响应请求的类,使用方法上的@RequestMapping
等注解去标明这个方法是用来响应哪个method
和path
。生成响应的整个过程,因为可能会涉及到很复杂的业务处理,又往下分出服务层,模型层等等,这里又涉及到@Service
,@Entity
等。
稍微复杂一点的Web项目,业务层之间互相依赖,为了面向扩展,Spring实现了类的实例的集中管理,在需要使用的地方,由框架自动将实例注入。这就是控制反转。这里涉及的是@AutoWired
等。
为了将与业务无关且比较全局的代码集中实现,如安全控制、日志记录、事务控制等,Spring又引入了AOP机制。这里涉及的是@Aspect
、@Before
、@After
等等。
所以自己去实现一个Java Web框架,也跑不了这些东西。
总的来说一个基本的框架思路是:
- 扫描包体下的所有类
- 识别出需要框架实例化集中管理的类并进行实例化
- 识别出需要使用aop增加的类,并使用cglib等技术生成扩展子类替换上一步中的实例
- 识别出需要自动注入实例的成员变量,并注入实例
- 识别出request-action的映射关系
- 构建一个Servlet分发器,完成request向action的分发
类的扫描与加载
获取当前线程的ClassLoader
,使用ClassLoader
的getResources
方法循环获取目录下的所有文件,遍历其中的 .class 文件。这里需要注意如果文件类型是 .jar,需要再深入进去,可以使用JarURLConnection
类。
Bean Container
识别出需要框架实例化并集中管理的类的前提是预先定义后标志符。比如使用@Controller,@Service两个注解去标识出这样的类。那么就可以拿上一步中扫描出来的class列表,去逐一判断是否使用了这两个注解(isAnnotationPresent
)。然后将需要实例化的类通过反射实例化并维持在一个单例的容器中,比如一个HashMap。
AOP增强
预先定义一个AOP基类,约束所有的AOP类都需要集成这个类,并重写这个类里的增强方法去实现增强。然后在这个类上约定使用注解的方式去定义增强的目标(@Aspect()
)。从类集合中得出这样的类,使用cglib提供的方法去实例化目标增强后的子类,然后放回到bean
容器中替换掉目标类,就实现了AOP。这里涉及到cglib的Enhancer.create
,intercept
等方法。关于AOP欢迎查看我之前些的这篇文章。
Requst-Action 映射关系
就像 spring 一样,我们约定所有的控制层都使用 @Controller
进行注解,在方法上通过@RequestMapping(method,path)
进行声明。那么这个映射关系的获取就比较简单了。
从类集合中获取 @Controller
注解的类,然后遍历下面的方法获取 @RequestMapping
注解的方法,从中获取出method
,path
,很容易就构建出这样的一套映射关系。
Servlet分发器
最后就是Servlet
分发了。定义一个HttpServlet
对象,接收所有请求。然后根据请求里的method
和path
,invoke
对应的controller
类的method
去执行,再将执行结果写到request
和response
中去。这样就实现了一个简单的分发器。
END
通过上面五步仅仅是建立了一个最简单的框架,还有许多细节、功能没有去考虑。欢迎讨论。源码见 github 。