手寫(xiě)web服務(wù)器:實(shí)現(xiàn)簡(jiǎn)單filter攔截邏輯

前言
今天早上起床的時(shí)候,我還在想應(yīng)該實(shí)現(xiàn)哪個(gè)組件,想了半天,發(fā)現(xiàn)基本上常用的注解組件都被我們給實(shí)現(xiàn)了(當(dāng)然,雖然都實(shí)現(xiàn)了,但是基本上都是簡(jiǎn)易版),最后想來(lái)想去覺(jué)得filter可以實(shí)現(xiàn)下,畢竟沒(méi)有這個(gè)模塊,項(xiàng)目中的就沒(méi)法實(shí)現(xiàn)權(quán)限控制了。
在開(kāi)始的時(shí)候,我已經(jīng)知道filter的難點(diǎn)是地址的匹配,也就是如何把我們配置的地址轉(zhuǎn)換為正則表達(dá)式,最后發(fā)現(xiàn)這塊涉及的知識(shí)點(diǎn)有點(diǎn)多,所以今天就只演示通配地址,即/*。
好了,我們一起來(lái)看下吧。
過(guò)濾器實(shí)現(xiàn)過(guò)程
定義注解
這里依然很輕車(chē)熟路,注解的配置我增加了很多屬性,最核心的就是urlPatterns,也就是我們的攔截地址。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebFilter {
String[] value() default {};
String description() default "";
String displayName() default "";
String[] urlPatterns() default {};
}
定義Filter接口
這里參考了javaEE的filter接口
public interface Filter {
default void init() {}
void doFilter(Request request, Response response, FilterChain filterChain) throws IOException;
default void destrory() {}
}
public interface FilterChain {
/**
* filter調(diào)用鏈
* @param request
* @param response
* @throws IOException
*/
void doFilter(Request request, Response response) throws IOException;
}
定義filter
加上webFilter,并配置攔截地址,實(shí)現(xiàn)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("過(guò)濾器TestFilter被訪問(wèn),攔截地址:%s", request.getRequestHear().getRequestMapping()));
if (Objects.nonNull(filterChain)) {
filterChain.doFilter(request, response);
}
}
}
過(guò)濾器初始化
這里主要是配合IoC,拿到過(guò)濾器的配置信息,并實(shí)例化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方法
這里就是對(duì)請(qǐng)求地址進(jìn)行過(guò)濾,當(dāng)匹配到請(qǐng)求時(shí),執(zhí)行匹配到過(guò)濾器的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);
}
}
}
測(cè)試
我們用瀏覽器訪問(wèn)任意地址,比如/testAutowire,會(huì)看到doFilter方法被執(zhí)行了,控制臺(tái)打印如下信息:

說(shuō)明,我們的filter已經(jīng)起作用了,是不是很簡(jiǎn)單呀。
總結(jié)
雖然filter的核心功能實(shí)現(xiàn)了,但作為一個(gè)合格的web服務(wù)器,攔截器也得夠健壯,夠靈活,所以還有很多工作要做:
比如要實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,就是實(shí)現(xiàn)多個(gè)過(guò)濾器的順序調(diào)用,層層調(diào)用,層層返回,形成調(diào)用鏈,這一塊后面要進(jìn)一步實(shí)現(xiàn);
另外一個(gè)問(wèn)題就是,我們要實(shí)現(xiàn)更靈活的地址匹配,前面我也說(shuō)了,目前的地址只實(shí)現(xiàn)了通配和嚴(yán)格匹配兩種,正則表達(dá)式支持的也不夠完美,這一塊也是后面要優(yōu)化的點(diǎn)。
好了,核心內(nèi)容到這里就結(jié)束了,但我想說(shuō)兩句閑話,這兩天在刷一個(gè)制作精良的劇——《覺(jué)醒年代》,這應(yīng)該是這幾年,我看過(guò)最有價(jià)值的電視劇了,讓我對(duì)很多革命先烈有了更深刻的認(rèn)識(shí),有興趣的小伙伴可以去看下,真的很贊。
下面是項(xiàng)目的開(kāi)源倉(cāng)庫(kù),有興趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推薦你自己動(dòng)個(gè)手,自己寫(xiě)一下,真的感覺(jué)不錯(cuò):
https://github.com/Syske/syske-boot

