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

铁佛  金牌会员 | 2024-5-19 21:53:25 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 888|帖子 888|积分 2664

创作缘由

平常使用 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 吗?
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <web-app>
  3.    
  4.     <filter>
  5.         <filter-name>LoggingFilter</filter-name>
  6.         <filter-class>com.github.houbb.minicat.support.filter.MyMiniCatLoggingHttpFilter</filter-class>
  7.     </filter>
  8.     <filter-mapping>
  9.         <filter-name>LoggingFilter</filter-name>
  10.         <url-pattern>/*</url-pattern>
  11.     </filter-mapping>
  12. </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
接口实现

简单的实现
  1. package com.github.houbb.minicat.support.filter;
  2. import com.github.houbb.log.integration.core.Log;
  3. import com.github.houbb.log.integration.core.LogFactory;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.ServletRequest;
  7. import javax.servlet.ServletResponse;
  8. import javax.servlet.http.HttpFilter;
  9. import java.io.IOException;
  10. /**
  11. *
  12. * @since 0.6.0
  13. */
  14. public class MyMiniCatLoggingHttpFilter extends HttpFilter {
  15.     private static final Log logger = LogFactory.getLog(MyMiniCatLoggingHttpFilter.class);
  16.     @Override
  17.     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  18.         logger.info("[MiniCat] MyMiniCatLoggingHttpFilter#doFilter req={}, resp={}", req, res);
  19.         super.doFilter(req, res, chain);
  20.     }
  21. }
复制代码
应用启动解析

DefaultFilterManager

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

以本地的 web.xml 解析为例
  1. /**
  2. * 处理 web 文件
  3. */
  4. protected void processWebXml() {
  5.     try {
  6.         SAXReader reader = new SAXReader();
  7.         Document document = reader.read(webXmlFile);
  8.         Element root = document.getRootElement();
  9.         // ...
  10.         //2. 处理 filter
  11.         final IFilterManager filterManager = this.miniCatContextConfig.getFilterManager();
  12.         processWebFilter(root, filterManager);
  13.         // ...
  14.     } catch (Exception e) {
  15.         throw new MiniCatException(e);
  16.     }
  17. }
复制代码
解析对应的 web.xml 标签内容,注册对应信息:
  1. protected void handleFilterConfigMap(Map<String, String> filterClassNameMap, Map<String, String> urlPatternMap, final IFilterManager filterManager) {
  2.     try {
  3.         for (Map.Entry<String, String> urlPatternEntry : urlPatternMap.entrySet()) {
  4.             String filterName = urlPatternEntry.getKey();
  5.             String urlPattern = urlPatternEntry.getValue();
  6.             String className = filterClassNameMap.get(filterName);
  7.             if (StringUtil.isEmpty(className)) {
  8.                 throw new MiniCatException("className not found for filterName: " + filterName);
  9.             }
  10.             Class servletClazz = Class.forName(className);
  11.             Filter httpServlet = (Filter) servletClazz.newInstance();
  12.             // 构建
  13.             String fullUrlPattern = buildFullUrlPattern(urlPattern);
  14.             filterManager.register(fullUrlPattern, httpServlet);
  15.         }
  16.     } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
  17.         throw new MiniCatException(e);
  18.     }
  19. }
复制代码
如许就可以后面调用了。
调用的机遇

我们什么时候调用呢?
当然是一个请求地点过来了,看是否有匹配的 filter,然后举行处理。
  1. @Override
  2. public void dispatch(final IMiniCatRequest request,
  3.                      final IMiniCatResponse response,
  4.                      final MiniCatContextConfig config) {
  5.     final IServletManager servletManager = config.getServletManager();
  6.     // 判断文件是否存在
  7.     String requestUrl = request.getUrl();
  8.     //before
  9.     List<Filter> filterList = config.getFilterManager().getMatchFilters(requestUrl);
  10.     // 获取请求分发
  11.     final IRequestDispatcher requestDispatcher = getRequestDispatcher(requestUrl);
  12.     // 请求前
  13.     filterList.forEach(filter -> {
  14.         try {
  15.             filter.doFilter(request, response, null);
  16.         } catch (IOException | ServletException e) {
  17.             throw new RuntimeException(e);
  18.         }
  19.     });
  20.     // 正式分发
  21.     requestDispatcher.dispatch(request, response, config);
  22. }
复制代码
如许,一个基础的 filter 功能就实现了。
开源地点
  1. /\_/\  
  2. ( o.o )
  3. > ^ <
复制代码
mini-cat 是简易版本的 tomcat 实现。别称【嗅虎】(心有猛虎,轻嗅蔷薇。)
开源地点:https://github.com/houbb/minicat

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

铁佛

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表