Java EE 领域,Spring 全家桶基本处于垄断地位。作为一个编程人员,用框架越久,就离底层越远(虽然我们离计算机底层已经十万八千里远了)。于是萌生了自己写一个 Java Web 框架的想法,花了些业务时间,写出来一个低仿低配版 Spring 。这篇文章从思路层面做一下总结。

概述

抛却框架,使用 java 去实现一个 Web 项目,核心是 Servlet。一个最简单的 Web 项目,可以直接继承HttpServlet,重写service之类的方法,并使用@WebServlet的注解指明urlPattern即可。

简单的去看一个 web 项目,就是拿到request,根据request的内容,比如methodpath,去查找并执行指定的业务逻辑,生成响应后,发送response

spring mvc中,使用@Controller注解去标明这是一个响应请求的类,使用方法上的@RequestMapping等注解去标明这个方法是用来响应哪个methodpath。生成响应的整个过程,因为可能会涉及到很复杂的业务处理,又往下分出服务层,模型层等等,这里又涉及到@Service@Entity等。

稍微复杂一点的Web项目,业务层之间互相依赖,为了面向扩展,Spring实现了类的实例的集中管理,在需要使用的地方,由框架自动将实例注入。这就是控制反转。这里涉及的是@AutoWired等。

为了将与业务无关且比较全局的代码集中实现,如安全控制、日志记录、事务控制等,Spring又引入了AOP机制。这里涉及的是@Aspect@Before@After等等。

所以自己去实现一个Java Web框架,也跑不了这些东西。

总的来说一个基本的框架思路是:

  • 扫描包体下的所有类
  • 识别出需要框架实例化集中管理的类并进行实例化
  • 识别出需要使用aop增加的类,并使用cglib等技术生成扩展子类替换上一步中的实例
  • 识别出需要自动注入实例的成员变量,并注入实例
  • 识别出request-action的映射关系
  • 构建一个Servlet分发器,完成request向action的分发

类的扫描与加载

获取当前线程的ClassLoader,使用ClassLoadergetResources方法循环获取目录下的所有文件,遍历其中的 .class 文件。这里需要注意如果文件类型是 .jar,需要再深入进去,可以使用JarURLConnection类。

Bean Container

识别出需要框架实例化并集中管理的类的前提是预先定义后标志符。比如使用@Controller,@Service两个注解去标识出这样的类。那么就可以拿上一步中扫描出来的class列表,去逐一判断是否使用了这两个注解(isAnnotationPresent)。然后将需要实例化的类通过反射实例化并维持在一个单例的容器中,比如一个HashMap。

AOP增强

预先定义一个AOP基类,约束所有的AOP类都需要集成这个类,并重写这个类里的增强方法去实现增强。然后在这个类上约定使用注解的方式去定义增强的目标(@Aspect())。从类集合中得出这样的类,使用cglib提供的方法去实例化目标增强后的子类,然后放回到bean容器中替换掉目标类,就实现了AOP。这里涉及到cglibEnhancer.createintercept等方法。关于AOP欢迎查看我之前些的这篇文章

Requst-Action 映射关系

就像 spring 一样,我们约定所有的控制层都使用 @Controller 进行注解,在方法上通过@RequestMapping(method,path)进行声明。那么这个映射关系的获取就比较简单了。

从类集合中获取 @Controller 注解的类,然后遍历下面的方法获取 @RequestMapping 注解的方法,从中获取出methodpath,很容易就构建出这样的一套映射关系。

Servlet分发器

最后就是Servlet分发了。定义一个HttpServlet对象,接收所有请求。然后根据请求里的methodpathinvoke对应的controller类的method去执行,再将执行结果写到requestresponse中去。这样就实现了一个简单的分发器。

END

通过上面五步仅仅是建立了一个最简单的框架,还有许多细节、功能没有去考虑。欢迎讨论。源码见 github