创作缘由

平时使用 tomcat 等 web 服务器不可谓不多,但是一直一知半解。

于是想着自己实现一个简单版本,学习一下 tomcat 的精髓。

系列教程

从零手写实现 apache Tomcat-01-入门介绍

从零手写实现 apache Tomcat-02-web.xml 入门详细介绍

从零手写实现 tomcat-03-基本的 socket 实现

从零手写实现 tomcat-04-请求和响应的抽象

从零手写实现 tomcat-05-servlet 处理支持

从零手写实现 tomcat-06-servlet bio/thread/nio/netty 池化处理

从零手写实现 tomcat-07-war 如何解析处理三方的 war 包?

从零手写实现 tomcat-08-tomcat 如何与 springboot 集成?

从零手写实现 tomcat-09-servlet 处理类

从零手写实现 tomcat-10-static resource 静态资源文件

从零手写实现 tomcat-11-filter 过滤器

从零手写实现 tomcat-12-listener 监听器

前言

还记得我们最初 web.xml 中的 filter 吗?

  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?> <web-app> <!-- Filter 配置 --> <filter> <filter-name>LoggingFilter</filter-name> <filter-class>com.github.houbb.minicat.support.filter.MyMiniCatLoggingHttpFilter</filter-class> </filter> <filter-mapping> <filter-name>LoggingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>

他的作用是什么?我们又该如何解析实现呢?

filter 是什么?

在Tomcat中,Filter可以被看作是一个网络请求的“安检员”。

就像你进火车站或机场前要经过安检一样,网络请求在到达它最终的目的地(比如一个Servlet)之前,也可以先经过一些Filter的处理。

Filter主要有以下几个作用:

过滤请求:Filter可以检查进入的请求,看看它是否满足某些条件。如果不满足,Filter可以拒绝这个请求,就像安检员发现你携带了违禁品,就不让你进站一样。

修改请求:除了检查请求,Filter还可以修改请求。比如,它可以添加一些请求头,或者改变请求参数等。

过滤响应:同样,Filter也可以检查服务器的响应,看看它是否满足某些条件。如果不满足,Filter可以修改这个响应。

修改响应:除了检查响应,Filter也可以修改响应的内容。比如,它可以添加一些额外的信息,或者改变响应的格式等。

tomcat 如何处理 filter 的?

客户端(比如浏览器)发送一个请求到Tomcat。

Tomcat的连接器(Connector)接收到这个请求。

请求首先经过所有的Filter链。每个Filter都有机会检查和修改这个请求。

一旦所有的Filter都处理完毕,请求就到达它的目标Servlet。

Servlet处理请求,并生成一个响应。

响应再次经过Filter链,每个Filter都有机会检查和修改这个响应。

最后,响应被发送回客户端。

自己实现

接口定义

这里就不定义了,直接复用 servlet 的标准 api

接口实现

简单的实现

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.github.houbb.minicat.support.filter; import com.github.houbb.log.integration.core.Log; import com.github.houbb.log.integration.core.LogFactory; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpFilter; import java.io.IOException; /** * * @since 0.6.0 */ public class MyMiniCatLoggingHttpFilter extends HttpFilter { private static final Log logger = LogFactory.getLog(MyMiniCatLoggingHttpFilter.class); @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { logger.info("[MiniCat] MyMiniCatLoggingHttpFilter#doFilter req={}, resp={}", req, res); super.doFilter(req, res, chain); } }

应用启动解析

DefaultFilterManager

定义一个 filter 的管理类

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.github.houbb.minicat.support.filter.manager; import com.github.houbb.heaven.util.lang.StringUtil; import com.github.houbb.log.integration.core.Log; import com.github.houbb.log.integration.core.LogFactory; import com.github.houbb.minicat.exception.MiniCatException; import com.github.houbb.minicat.support.servlet.manager.DefaultServletManager; import javax.servlet.Filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * filter 管理 * * @since 0.6.0 * @author 老马啸西风 */ public class DefaultFilterManager implements IFilterManager { //... 省略基础属性 @Override public void register(String url, Filter filter) { logger.info("[MiniCat] register Filter, url={}, Filter={}", url, filter.getClass().getName()); filterMap.put(url, filter); } @Override public Filter getFilter(String url) { return filterMap.get(url); } @Override public List<Filter> getMatchFilters(String url) { List<Filter> resultList = new ArrayList<>(); for(Map.Entry<String, Filter> entry : filterMap.entrySet()) { String urlPattern = entry.getKey(); if(url.matches(urlPattern)) { resultList.add(entry.getValue()); } } return resultList; } }

register 的时机

以本地的 web.xml 解析为例

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** * 处理 web 文件 */ protected void processWebXml() { try { SAXReader reader = new SAXReader(); Document document = reader.read(webXmlFile); Element root = document.getRootElement(); // ... //2. 处理 filter final IFilterManager filterManager = this.miniCatContextConfig.getFilterManager(); processWebFilter(root, filterManager); // ... } catch (Exception e) { throw new MiniCatException(e); } }

解析对应的 web.xml 标签内容,注册对应信息:

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void handleFilterConfigMap(Map<String, String> filterClassNameMap, Map<String, String> urlPatternMap, final IFilterManager filterManager) { try { for (Map.Entry<String, String> urlPatternEntry : urlPatternMap.entrySet()) { String filterName = urlPatternEntry.getKey(); String urlPattern = urlPatternEntry.getValue(); String className = filterClassNameMap.get(filterName); if (StringUtil.isEmpty(className)) { throw new MiniCatException("className not found for filterName: " + filterName); } Class servletClazz = Class.forName(className); Filter httpServlet = (Filter) servletClazz.newInstance(); // 构建 String fullUrlPattern = buildFullUrlPattern(urlPattern); filterManager.register(fullUrlPattern, httpServlet); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new MiniCatException(e); } }

这样就可以后面调用了。

调用的时机

我们什么时候调用呢?

当然是一个请求地址过来了,看是否有匹配的 filter,然后进行处理。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override public void dispatch(final IMiniCatRequest request, final IMiniCatResponse response, final MiniCatContextConfig config) { final IServletManager servletManager = config.getServletManager(); // 判断文件是否存在 String requestUrl = request.getUrl(); //before List<Filter> filterList = config.getFilterManager().getMatchFilters(requestUrl); // 获取请求分发 final IRequestDispatcher requestDispatcher = getRequestDispatcher(requestUrl); // 请求前 filterList.forEach(filter -> { try { filter.doFilter(request, response, null); } catch (IOException | ServletException e) { throw new RuntimeException(e); } }); // 正式分发 requestDispatcher.dispatch(request, response, config); }

这样,一个基础的 filter 功能就实现了。

开源地址

  [plaintext]
1
2
3
/\_/\ ( o.o ) > ^ <

mini-cat 是简易版本的 tomcat 实现。别称【嗅虎】(心有猛虎,轻嗅蔷薇。)

开源地址:https://github.com/houbb/minicat

参考资料

https://www.cnblogs.com/yuhushen/p/15396612.html