手写web服务器:实现简单filter拦截逻辑

云中志

共 6351字,需浏览 13分钟

 · 2021-06-12

前言

今天早上起床的时候,我还在想应该实现哪个组件,想了半天,发现基本上常用的注解组件都被我们給实现了(当然,虽然都实现了,但是基本上都是简易版),最后想来想去觉得filter可以实现下,毕竟没有这个模块,项目中的就没法实现权限控制了。

在开始的时候,我已经知道filter的难点是地址的匹配,也就是如何把我们配置的地址转换为正则表达式,最后发现这块涉及的知识点有点多,所以今天就只演示通配地址,即/*

好了,我们一起来看下吧。

过滤器实现过程

定义注解

这里依然很轻车熟路,注解的配置我增加了很多属性,最核心的就是urlPatterns,也就是我们的拦截地址。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebFilter {
    String[] value() default {};

    String description() default "";

    String displayName() default "";

    String[] urlPatterns() default {};
}

定义Filter接口

这里参考了javaEEfilter接口

public interface Filter {
    default void init() {}

    void doFilter(Request request, Response response, FilterChain filterChain) throws IOException;

    default void destrory() {}
}

public interface FilterChain {
    /**
     * filter调用链
     * @param request
     * @param response
     * @throws IOException
     */

    void doFilter(Request request, Response response) throws IOException;
}

定义filter

加上webFilter,并配置拦截地址,实现doFilter方法

@WebFilter(urlPatterns = {"/*"}, description = "test filter")
public class TestFilter implements Filter{
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) throws IOException {
        System.out.println(String.format("过滤器TestFilter被访问,拦截地址:%s", request.getRequestHear().getRequestMapping()));
        if (Objects.nonNull(filterChain)) {
            filterChain.doFilter(request, response);
        }
    }
}

过滤器初始化

这里主要是配合IoC,拿到过滤器的配置信息,并实例化filter

private static Map<Filter, String[]> filterUrlPatternsMap = Maps.newHashMap();

    private static LinkedList<Filter> filterLinkedList = Lists.newLinkedList();

    public static Map<Filter, String[]> getFilterUrlPatternsMap() {
        return filterUrlPatternsMap;
    }

    public static LinkedList<Filter> getFilterLinkedList() {
        return filterLinkedList;
    }

    public static void init(Class zClass) throws IllegalAccessException, InstantiationException {
        Annotation webFilter = zClass.getAnnotation(WebFilter.class);
        if (Objects.nonNull(webFilter)) {
            String[] urlPatterns = ((WebFilter) webFilter).urlPatterns();
            Filter filter = (Filter)zClass.newInstance();
            filterUrlPatternsMap.put(filter, urlPatterns);
            filterLinkedList.add(filter);
        }
    }

修改disPatcher方法

这里就是对请求地址进行过滤,当匹配到请求时,执行匹配到过滤器的doFilter方法

Map<Filter, String[]> filterUrlPatternsMap = FilterHandler.getFilterUrlPatternsMap();
            LinkedList<Filter> filterLinkedList = FilterHandler.getFilterLinkedList();
            ListIterator<Filter> filterListIterator = filterLinkedList.listIterator();
            while (filterListIterator.hasNext()) {
                Filter filter = filterListIterator.next();
                String[] values = filterUrlPatternsMap.get(filter);
                for (String value : values) {
                    if(Pattern.matches(value.replace('/''.'), requestMapping)) {
                        filter.doFilter(request, response, null);
                    }

                }
            }

测试

我们用浏览器访问任意地址,比如/testAutowire,会看到doFilter方法被执行了,控制台打印如下信息:

说明,我们的filter已经起作用了,是不是很简单呀。

总结

虽然filter的核心功能实现了,但作为一个合格的web服务器,拦截器也得够健壮,够灵活,所以还有很多工作要做:

比如要实现链式调用,就是实现多个过滤器的顺序调用,层层调用,层层返回,形成调用链,这一块后面要进一步实现;

另外一个问题就是,我们要实现更灵活的地址匹配,前面我也说了,目前的地址只实现了通配和严格匹配两种,正则表达式支持的也不够完美,这一块也是后面要优化的点。

好了,核心内容到这里就结束了,但我想说两句闲话,这两天在刷一个制作精良的剧——《觉醒年代》,这应该是这几年,我看过最有价值的电视剧了,让我对很多革命先烈有了更深刻的认识,有兴趣的小伙伴可以去看下,真的很赞。

下面是项目的开源仓库,有兴趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推荐你自己动个手,自己写一下,真的感觉不错:

https://github.com/Syske/syske-boot
- END -


浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报