You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
9.9 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 10 | 比较Jetty架构特点之Handler组件
在专栏上一期我们学习了Jetty的整体架构。先来回顾一下Jetty 就是由多个Connector连接器、多个Handler处理器以及一个线程池组成整体结构图如下。
![](https://static001.geekbang.org/resource/image/66/6a/66e55e89fd621c0eba6321471da2016a.png)
上一期我们分析了Jetty Connector组件的设计Connector会将Servlet请求交给Handler去处理那Handler又是如何处理请求的呢
Jetty的Handler在设计上非常有意思可以说是Jetty的灵魂Jetty通过Handler实现了高度可定制化那具体是如何实现的呢我们能从中学到怎样的设计方法呢接下来我就来聊聊这些问题。
## Handler是什么
**Handler就是一个接口它有一堆实现类**Jetty的Connector组件调用这些接口来处理Servlet请求我们先来看看这个接口定义成什么样子。
```
public interface Handler extends LifeCycle, Destroyable
{
//处理请求的方法
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
//每个Handler都关联一个Server组件被Server管理
public void setServer(Server server);
public Server getServer();
//销毁方法相关的资源
public void destroy();
}
```
你会看到Handler接口的定义非常简洁主要就是用handle方法用来处理请求跟Tomcat容器组件的service方法一样它有ServletRequest和ServletResponse两个参数。除此之外这个接口中还有setServer和getServer方法因为任何一个Handler都需要关联一个Server组件也就是说Handler需要被Server组件来管理。一般来说Handler会加载一些资源到内存因此通过设置destroy方法来销毁。
**Handler继承关系**
Handler只是一个接口完成具体功能的还是它的子类。那么Handler有哪些子类呢它们的继承关系又是怎样的这些子类是如何实现Servlet容器功能的呢
Jetty中定义了一些默认Handler类并且这些Handler类之间的继承关系比较复杂我们先通过一个全景图来了解一下。为了避免让你感到不适我对类图进行了简化。
![](https://static001.geekbang.org/resource/image/3a/64/3a7b3fbf16bb79594ec23620507c5c64.png)
从图上你可以看到Handler的种类和层次关系还是比较复杂的
Handler接口之下有抽象类AbstractHandler这一点并不意外因为有接口一般就有抽象实现类。
在AbstractHandler之下有AbstractHandlerContainer为什么需要这个类呢这其实是个过渡为了实现链式调用一个Handler内部必然要有其他Handler的引用所以这个类的名字里才有Container意思就是这样的Handler里包含了其他Handler的引用。
理解了上面的AbstractHandlerContainer我们就能理解它的两个子类了HandlerWrapper和HandlerCollection。简单来说就是HandlerWrapper和HandlerCollection都是Handler但是这些Handler里还包括其他Handler的引用。不同的是HandlerWrapper只包含一个其他Handler的引用而HandlerCollection中有一个Handler数组的引用。
![](https://static001.geekbang.org/resource/image/f8/c1/f89a1e88a78a7e6860d69af3572467c1.png)
接着来看左边的HandlerWrapper它有两个子类Server和ScopedHandler。Server比较好理解它本身是Handler模块的入口必然要将请求传递给其他Handler来处理为了触发其他Handler的调用所以它是一个HandlerWrapper。
再看ScopedHandler它也是一个比较重要的Handler实现了“具有上下文信息”的责任链调用。为什么我要强调“具有上下文信息”呢那是因为Servlet规范规定Servlet在执行过程中是有上下文的。那么这些Handler在执行过程中如何访问这个上下文呢这个上下文又存在什么地方呢答案就是通过ScopedHandler来实现的。
而ScopedHandler有一堆的子类这些子类就是用来实现Servlet规范的比如ServletHandler、ContextHandler、SessionHandler、ServletContextHandler和WebAppContext。接下来我会详细介绍它们但我们先把总体类图看完。
请看类图的右边跟HandlerWrapper对等的还有HandlerCollectionHandlerCollection其实维护了一个Handler数组。你可能会问为什么要发明一个这样的Handler这是因为Jetty可能需要同时支持多个Web应用如果每个Web应用有一个Handler入口那么多个Web应用的Handler就成了一个数组比如Server中就有一个HandlerCollectionServer会根据用户请求的URL从数组中选取相应的Handler来处理就是选择特定的Web应用来处理请求。
**Handler的类型**
虽然从类图上看Handler有很多但是本质上这些Handler分成三种类型
* 第一种是**协调Handler**这种Handler负责将请求路由到一组Handler中去比如上图中的HandlerCollection它内部持有一个Handler数组当请求到来时它负责将请求转发到数组中的某一个Handler。
* 第二种是**过滤器Handler**这种Handler自己会处理请求处理完了后再把请求转发到下一个Handler比如图上的HandlerWrapper它内部持有下一个Handler的引用。需要注意的是所有继承了HandlerWrapper的Handler都具有了过滤器Handler的特征比如ContextHandler、SessionHandler和WebAppContext等。
* 第三种是**内容Handler**说白了就是这些Handler会真正调用Servlet来处理请求生成响应的内容比如ServletHandler。如果浏览器请求的是一个静态资源也有相应的ResourceHandler来处理这个请求返回静态页面。
## 如何实现Servlet规范
上文提到ServletHandler、ContextHandler以及WebAppContext等它们实现了Servlet规范那具体是怎么实现的呢为了帮助你理解在这之前我们还是来看看如何使用Jetty来启动一个Web应用。
```
//新建一个WebAppContextWebAppContext是一个Handler
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/mywebapp");
webapp.setWar("mywebapp.war");
//将Handler添加到Server中去
server.setHandler(webapp);
//启动Server
server.start();
server.join();
```
上面的过程主要分为两步:
第一步创建一个WebAppContext接着设置一些参数到这个Handler中就是告诉WebAppContext你的WAR包放在哪Web应用的访问路径是什么。
第二步就是把新创建的WebAppContext添加到Server中然后启动Server。
WebAppContext对应一个Web应用。我们回忆一下Servlet规范中有Context、Servlet、Filter、Listener和Session等Jetty要支持Servlet规范就需要有相应的Handler来分别实现这些功能。因此Jetty设计了3个组件ContextHandler、ServletHandler和SessionHandler来实现Servlet规范中规定的功能而**WebAppContext本身就是一个ContextHandler**另外它还负责管理ServletHandler和SessionHandler。
我们再来看一下什么是ContextHandler。ContextHandler会创建并初始化Servlet规范里的ServletContext对象同时ContextHandler还包含了一组能够让你的Web应用运行起来的Handler可以这样理解Context本身也是一种Handler它里面包含了其他的Handler这些Handler能处理某个特定URL下的请求。比如ContextHandler包含了一个或者多个ServletHandler。
再来看ServletHandler它实现了Servlet规范中的Servlet、Filter和Listener的功能。ServletHandler依赖FilterHolder、ServletHolder、ServletMapping、FilterMapping这四大组件。FilterHolder和ServletHolder分别是Filter和Servlet的包装类每一个Servlet与路径的映射会被封装成ServletMapping而Filter与拦截URL的映射会被封装成FilterMapping。
SessionHandler从名字就知道它的功能用来管理Session。除此之外WebAppContext还有一些通用功能的Handler比如SecurityHandler和GzipHandler同样从名字可以知道这些Handler的功能分别是安全控制和压缩/解压缩。
WebAppContext会将这些Handler构建成一个执行链通过这个链会最终调用到我们的业务Servlet。我们通过一张图来理解一下。
![](https://static001.geekbang.org/resource/image/5f/c1/5f1404567deec36ac68c36e44bb06cc1.jpg)
通过对比Tomcat的架构图你可以看到Jetty的Handler组件和Tomcat中的容器组件是大致是对等的概念Jetty中的WebAppContext相当于Tomcat的Context组件都是对应一个Web应用而Jetty中的ServletHandler对应Tomcat中的Wrapper组件它负责初始化和调用Servlet并实现了Filter的功能。
对于一些通用组件比如安全和解压缩在Jetty中都被做成了Handler这是Jetty Handler架构的特点。
因此对于Jetty来说请求处理模块就被抽象成Handler不管是实现了Servlet规范的Handler还是实现通用功能的Handler比如安全、解压缩等我们可以任意添加或者裁剪这些“功能模块”从而实现高度的可定制化。
## 本期精华
Jetty Server就是由多个Connector、多个Handler以及一个线程池组成。
Jetty的Handler设计是它的一大特色Jetty本质就是一个Handler管理器Jetty本身就提供了一些默认Handler来实现Servlet容器的功能你也可以定义自己的Handler来添加到Jetty中这体现了“**微内核 + 插件**”的设计思想。
## 课后思考
通过今天的学习我们知道各种Handler都会对请求做一些处理再将请求传给下一个Handler而Servlet也是用来处理请求的那Handler跟Servlet有什么区别呢
不知道今天的内容你消化得如何?如果还有疑问,请大胆的在留言区提问,也欢迎你把你的课后思考和心得记录下来,与我和其他同学一起讨论。如果你觉得今天有所收获,欢迎你把它分享给你的朋友。