Spring filter原理深入浅出
在这篇文章中,我会介绍一个请求从浏览器进来,是如何被Spring拦截到的,Spring又是如何处理这个请求的,filter有什么作用,filter的原理等
一、前置知识 OK,我们首先介绍一下,请求从浏览器过来,我们的服务器会经历些什么。先看一张jetty的内部结构图
我们看到jetty的核心组件包括了一大堆的*Handler
,对于这些handler,不论任何容器基本都有,可能名称不太相同,笔者这里以undertow这个容器为例,浏览器发送过来的请求,会经过一个FilterHandler
这个handler跟我们今天的主题相关。
二、Request阶段 先看一下这个handler的处理代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void handleRequest (HttpServerExchange exchange) throws Exception {
ServletRequestContext servletRequestContext = (ServletRequestContext)exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
ServletRequest request = servletRequestContext.getServletRequest();
ServletResponse response = servletRequestContext.getServletResponse();
DispatcherType dispatcher = servletRequestContext.getDispatcherType();
Boolean supported = (Boolean)this .asyncSupported.get(dispatcher);
if (supported != null && !supported.booleanValue()) {
servletRequestContext.setAsyncSupported(false );
}
List<ManagedFilter> filters = (List)this .filters.get(dispatcher);
if (filters == null ) {
this .next.handleRequest(exchange);
} else {
FilterHandler.FilterChainImpl filterChain = new FilterHandler.FilterChainImpl(exchange, filters, this .next, this .allowNonStandardWrappers);
filterChain.doFilter(request, response);
}
}
前面都是拿request和response我们不用看,主要是看后面几行代码,filters
如果我们有配置自己的filter,这里肯定不会为空,那么就走下面的filter 链 ,我们看下FilterHandler里面的doFilter的代码
1
2
3
4
5
6
int index = this .location++;
if (index >= this .filters.size()) {
this .next.handleRequest(this .exchange);
} else {
((ManagedFilter)this .filters.get(index)).doFilter(request, response, this );
}
location默认是0,所以会从list里面的第一个元素取起,依次执行,OK,我们知道,filter里面一般会有一个filterChain.doFilter(request, response);
这句话,代表filter链继续执行,这时候,这里的location就会+1 ,变成执行第二个,第三个,链条就转动起来了。终于执行到我们自己写的ResponseWrapperFilter
,这个filter大家可以在我后面的源码里看到,这里我先说有这么一个自己写的filter,我用来包装我们的response,给response做些手脚。哈哈。
OK,我们继续,当所有的filter都执行完毕之后,就会开始执行this.next.handleRequest(exchange); 这段代码,也就是在继续执行我们刚才的handler链,next表示的是ServletHandler` 这个类,执行代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
servlet = this .managedServlet.getServlet();
((Servlet)servlet.getInstance()).service(request, response);
} catch (UnavailableException var12) {
if (var12.isPermanent()) {
UndertowServletLogger.REQUEST_LOGGER.stoppingServletDueToPermanentUnavailability(this .managedServlet.getServletInfo().getName(), var12);
this .managedServlet.stop();
this .managedServlet.setPermanentlyUnavailable(true );
exchange.setStatusCode(404 );
} else {
unavailableUntilUpdater.set(this , System.currentTimeMillis() + (long )(var12.getUnavailableSeconds() * 1000 ));
UndertowServletLogger.REQUEST_LOGGER.stoppingServletUntilDueToTemporaryUnavailability(this .managedServlet.getServletInfo().getName(), new Date(until), var12);
exchange.setStatusCode(503 );
}
一旦掉用了下面这段代码,我们的处理逻辑就会被调用,说白了就是我们Controller里面的那些逻辑就会被调用
1
((Servlet)servlet.getInstance()).service(request, response);
这段代码很重要
三、reponse阶段 经过了上面这段代码的执行,我们的filter开始收拢了,所有的filter在执行filterChain.doFilter(request, response);
这段代码之后其实代码是还没有执行完的,如果你下面还有写代码的话,大家好好品味一下这句话,是不是这个意思。因为每个filter都去调用下一个filter了,等最后一个filter执行完毕之后,就开始慢慢收拢,这时候我们的reponse也有了。
当所有的filter执行完毕自己的后置代码之后,就回到了我们最初的地方,我们的最初的FilterHandler开始调用的地方。undertow服务器就继续开始执行了,它就会执行下面的代码输出你想要的信息
1
2
3
4
if (!exchange.isDispatched() && !(exchange.getConnection() instanceof ServletInitialHandler.MockServerConnection)) {
servletRequestContext.getOriginalResponse().responseDone();
servletRequestContext.getOriginalRequest().clearAttributes();
}
主要就是把输出流写入到socket中,然后客户端接收
好啦,今天就讲到这里啦,这文章还没有结束,请期待后续 ^ _ ^