学写一个 Java Web MVC 框架

Posted sp42a

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学写一个 Java Web MVC 框架相关的知识,希望对你有一定的参考价值。

访问请求处理

当客户端发送一个请求,被自定义的过滤器MvcDispatcher拦截,解析请求地址和参数对象跳转到一个控制器的方法中,然后执行进行逻辑处理后返回响应内容给MvcDispatcher输出,这样MVC逻辑就走完了。前期的初始化过程是为这里做准备。当接收到请求以后,进行拦截的方法如插图所示。
在这里插入图片描述

doFilter()实际上主要做了几件事:

  • 首先,它会判断是否静态资源的请求,若是则不走后面的流程;
  • 其次,加入一些实用功能,例如增强request、response,把它们打包成更易使用的MvcRequest、MvcOutput对象;还有记录一下请求时间、把request、response保存ThreadLocal等等;
  • 然后,匹配路由路径找到控制器方法并执行;
  • 执行方法期间,会检查是否有FilterAction过滤器,有就执行;
  • 假如在上面的过程中出现异常,则会触发MvcDispatcher处理异常的过程。

针对以上几点下面展开来深入了解。

如何处理静态网页?

由于MVC系统试图将过滤器的url-pattern映射为“/”,即访问的 URL都将由 MvcDispatcher处理,包括所有静态文件。所谓静态文件就是将服务器本地的文件原样输出到客户端,例如静态html网页、JPG/PNG/GIF等的图片、CSS其他无须服务端动态处理内容的那些文件或资源。

如前面插图所示,早在doFilter()的第一步将试图按URL查找是否匹配静态文件,它是ServletHelper的isStaticAsset()方法。如插图所示,该方法非常简单,主要通过正则表达式来判断URL后缀是否静态文件。

在这里插入图片描述

对于正则对象,应尽早编译为Pattern对象并将它作为静态成员出现,只需要创建一次,可供之后反复使用。

增强MvcRequest、MvcOutput对象

毋庸多言Request、Response对象是Web开发中非常常见和熟悉的对象。为了避免直接使用Request、Response而产生重复代码,有必要把这些方法封装起来。可以写成一个个静态的小函数,不过我们建议那么做。事实上Servlet本身有个不错的方案,那便是利用HttpServletRequestWrapperHttpServletResponseWrapper包装了原Request、Response对象。虽说是包装器但它实际为装饰模式(Decorator Pattern)之应用。装饰模式是经典设计模式里的一种,指在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。所谓包装的实现是通过继承来实现的,如果要修改某个方法,通过覆盖旧方法即可。

MvcReqeust就是基于HttpServletRequest实现新功能的这么一个子类,它继承于HttpServletRequestWrapper,然后实例化时候必须对构造器传参,就是原来的HttpServletRequest对象,如插图所示。
在这里插入图片描述

下面通过一个获取当前URL请求路径的例子来说明该扩展模式。默认request的getRequestURI()可以获取请求地址,但如果在Servlet+JSP下,缺省的是JSP磁盘文件所在的地址。我们希望改过来,应是获取请求URL地址,如插图所示。
在这里插入图片描述

这个新的方法我们并不打算重新起名字,而是直接重写覆盖原先的HttpServletRequest的getRequestURI()方法,故打上@Override注解说明之。怎么获取真实请求地址呢?读取常量javax.servlet.forward.request_uri即可,这里调用了request另外一个方法getAttribute()获取常量。如果为空则读取父类的getRequestURI()方法,也就是说,虽然覆盖了getRequestURI()但通过super关键字还是可以调用父类的方法,那样就不影响了原对象的逻辑而又较清晰地实现了解耦。

ThreadLocal应用

前面我们说到,控制器方法需要什么参数,取决于开发者如何定义。假设一个控制器方法没有定义request参数,那该如何获取呢?request/response这类对象是如此地常用以致于把它们安排到ThreadLocal中,让控制器能轻易地访问到这些对象。向ThreadLocal这个容器存储的对象,在当前线程范围内都可以取得出来,类似于全局变量,如下例所示。

@Path("/bar")
public class BarController implements IController {
	@GET
	public String showHTML() {
		MvcRequest request = MvcRequest.getHttpServletRequest();
		return "html::Hello World! " + request.getParameter("foo");
	}
}

关于ThreadLocal的深入应用,在介绍数据库连接对象的时候还会再说。

仿AOP的过滤器

提到过滤器,我们第一时间可能会想到的是Servlet里面的Filter原生过滤器,而我们统一接受请求的MvcDispatcher也正是一个标准的过滤器应用——只不过它扮演了控制器也就是原先HttpServlet的作用。原先控制器是有了,那么过滤器本身这个功能点呢?虽然说在MVC框架仍旧可以使用标准的Servlet Filter,但是结合MVC框架而言,已经内置了过滤器的功能,和控制器本身结合更简单自然。实际上,这种过滤器更类似于AOP拦截器,它会在控制器执行之前发生(前置)又可以在执行控制器之后执行一个方法(后置),插入的时间点是方法执行的一前一后,而且另外不同的是,它基于Java注解的写法。所以综合来讲说有点像AOP。

过滤器本身,常常被用来实现下面的功能:1、页面授权根,据登录用户的权限,阻止或许可用户访问特定的页面;2、日志和审计记录和检查用户访问WEB应用的情况;3、本地化,显示本地语言和风格的页面;4、高速缓存,页面静态化,提高响应速度。

客户端发起一次HTTP请求,对应查找有Action对象,Action返回控制器实例和控制器方法,有这两个条件就可以执行控制器方法了吗?还不行,因为还需要执行方法的参数。

以上是关于学写一个 Java Web MVC 框架的主要内容,如果未能解决你的问题,请参考以下文章

学写一个 Java Web MVC 框架

学写一个 Java Web MVC 框架

学写一个 Java Web MVC 框架

Java Web ——MVC基础框架讲解及代码演示

Java Web ——MVC基础框架讲解及代码演示

Java Web ——MVC基础框架讲解及代码演示