学写一个 Java Web MVC 框架

Posted sp42a

tags:

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

解析控制器

之所以要解析控制器,是因为Controller类定义了各种URL映射的规则。在初始化阶段我们要获取这套规则具体如何,以便每次请求访问过来的时候可以执行具体的控制器方法。从某个角度讲这些控制器类相当于一个个配置文件,配置规则利用Java注解来参与完成。很多场合下注解发挥了配置的作用。Controller注解可以定义在类身上,可以定义在方法身上,开发者先定义Controller类和方法,然后按照规则需求分配不同的注解到具体的类或者方法身上,从而完成URL与控制器之间的映射关系,这正是MVC的所要完成的目标:当用户访问一个URL,最终对应的是一个Java方法,用户获得什么信息取决于该方法返回什么,那自然是一套规则,视乎Controller API能够提供什么能力给我们去设置这套规则。

如下是一个典型的Controller。

@Path("/MyTopPath_And_SubPath")
public class SubPathController implements IController {
	@GET
	public String showhtml() {
		return "html::Hello World!";
	}

	@GET
	@Path("subPath")
	public String showHi(HttpServletRequest request, HttpServletResponse response)  {
		return "html::Hello " + request.getParameter("name");
	}
}

Controller目标已经明确,就是映射URL与Controller的关系。为此必须使用一种数据结构来存储这种关系。到底应该如何设计合适的数据结构来描述映射关系呢?这将是我们MVC的重点所在。

首先观察Action结构。控制器与方法一起构成了一个Action对象,即action = controller + methods。我们解析一个控制器所有的信息,将其保存在Action身上。一个Action有如下属性。

  • 一个控制器对应着一个Action,如下所示Action类有IController controller属性;
  • 控制器注解@Path有URL路径,故如下所示Action类有String path属性;
  • 控制器方法有注解@GET@POST等四种常见HTTP方法,故如下所示Action类有Method getMethod、postMethod、putMethod、deleteMethod属性;
  • 控制器控制器的方法还可以有注解@Path子路径,故如下所示Action类有Map<String, Action> children属性;

Action通过children属性扩展下一级路径,形成一颗Action树。children本身是一个Map,键是URL路径的各个目录,键值是路径对应的Action。如下面控制器 FooController对应树状图,如插图所示。

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;

@Path("/foo")
public class FooController implements IController {
	@GET
	public String get();
	
	@POST
	public String post();
	
	@PUT
	public String put();
	
	@DELETE
	public String delete();
	
	@Path("/bar")
	@GET
	public String barGet();
	
	@Path("/bar")
	@DELETE
	public String barDELETE();
	
	@Path("/xyz")
	@GET
	public String xyzGET();	
	
	@Path("/xyz/bar/foo")
	@POST
	public String xyzPOST();	
}

在这里插入图片描述

控制器FooController一开始的根目录为foo,有下级目录/foo/bar、/foo/xyz和/foo/xyz/bar/foo。篇幅所限,最后的/xyz/bar/foo没有在图中列出。如果要列出,还有树节点/foo/xyz/bar和/foo/xyz/bar/foo这两个,其中/foo/xyz/bar节点有一Action实例,但是其属性getMethod、postMethod、putMethod、deleteMethod皆为null,仅承托下一级树节点之用;节点/foo/xyz/bar/foo有一属性 postMethod,因为它本身对应有POST的方法。

所有控制器路径与Action的关系,都定义在一个全局的静态成员urlMappingTree身上,它是一个Action Map,如下插图所示。
在这里插入图片描述

控制器的分析工作在Servlet过滤器初始化阶段完成,最终结果就是形成这个urlMappingTree(有读者可能会问,Tomcat的webapps下面可以部署多个项目,在不同的目录。假设修改项目A的静态static成员,会影响别的项目的static成员吗?Tomcat对每一个项目使用一个类加载器。JVM中判断一个类是否相同是两个条件,全限定名相同,类加载器相同。因此这种情况是互相隔离的,不用担心)的Map结构。分析工作是怎么完成的呢?回到ControllerScanner类的add()方法,开始了对控制器的相关解析工作,返回一个Action并保存在urlMappingTree,如插图示。
在这里插入图片描述

第一个重点方法是findTreeByPath(),这里调用的是重载后的方法,其原型方法如下插图所示。
在这里插入图片描述

参数path是URL路径转换为队列(Queen)。队列的特性是在列表的头端进行删除操作,而在列表的后端进行插入操作。一开始路径是String,通过字符串split()分拆为数组后,转换为LinkedList返回。LinkedList也是一种Queen对象。

对于Map树要进行遍历查找目标Action,输入条件是path。当前初始化工作是新建Action节点,也就是说找不到目标Action就会新建Action。这里应用了数据结构队列的知识,当path队列压出一元素,马上进行Map.get(path[0])的匹配,若为null则表示接下来要新建Action,否则是已经存在的Action。然后检查这队列是否已经为空(path.isEmpty()),若为空表示已经查找完毕,返回target即可,否则仍要查找下一级目录。这时队列已经压出头端的元素,剩下未查找的目录元素,则再次调用本方法,即递归,直到找到匹配路径为止,或者找不到返回null。在递归之前,要检查一下children属性是否已有Map实例。

之所以要提供boolean createIfEmpty参数,是为了后来MvcDispatcher命中路径时做的准备。那时候,Action已经初始化完毕了,每次请求过来分析路径查找Action,不再有任何新建的操作,故只是单纯的查找操作。

生成Action对象之后,add()接着执行的方法是创建控制器实例createControllerInstance()和解析控制器方法parseMethod()。创建实例应用到了反射和依赖注射的概念,下面相关章节会讲,这里先定懂点谈谈parseMethod(),如插图所示。

在这里插入图片描述

通过反射可以获取控制器方法所有的方法(getMethods()),但只带有@GET/@POST/@PUT/@DELETE注解的方法才会被解析,表明是HTTP请求的方法。另外一种情形是:带@Path组件的和不带的,前者表示当前路径下的方法;后者为子路径的方法,由控制器总路径加上子路径组成最终的目标路径,子路径一样可以有自己的GET/POST/PUT/DELETE方法。

children是子路径保存所在Map之引用,创建好的subAction会保存于此。subAction的controller指针会指向当前控制器实例。

methodSend()的作用是根据方法当前的@GET/@POST/@PUT/@DELETE注解分门别类地指向对应的Method方法,以便MVC有请求到达时执行对应的控制器方法。这一过程比较简单就不张贴代码了。

分析控制器的过程到这里基本完成了,整个过滤器的初始化init()工作到这里也结束了。总的来说,Map结构的urlMappingTree保存了所有控制器路由与执行方法的映射关系,它是一棵树,如果对其进行遍历查找匹配的路径成为了一个关键算法,这里简单地应用到队列的知识,是理解MVC路由匹配的关键。除此之外,对于相关注解的使用运用到了Java反射的知识。

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

学写一个 Java Web MVC 框架

学写一个 Java Web MVC 框架

学写一个 Java Web MVC 框架

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

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

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