輸入過濾器——InputFilter,過濾器inputfilter
一般情況下我們通過請求體讀取器InputStreamInputBuffer擷取的僅僅是來源資料,即未經過任何處理髮送方發來的位元組。但有些時候在這個讀取的過程中希望做一些額外的處理,並且這些額外處理可能是根據不同條件做不同的處理,考慮到程式解耦與擴充,於是引入過濾器(過濾器模式)——輸入過濾器InputFilter。在讀取資料過程中對於額外的操作只需要通過添加不同的過濾器即可實現,例如添加對http1.1協議分塊傳輸的相關操作的過濾器。
如,在通訊端輸入緩衝裝置中,從作業系統底層讀取的位元組會緩衝在buf中,請求行和要求標頭部被解析後緩衝區buf的指標指向請求體起始位置,通過請求體讀取器InputStreamInputBuffer可進行讀取操作,它會自動判定buf是否已經讀完,讀完則重新從作業系統底層讀取位元組到buf。當其他組件從通訊端輸入緩衝裝置讀取請求體時,裝置將判定是否包含過濾器,假設有則通過一層層的過濾器完成過濾操作後才能到desBuf,這個過程就像被加入了一道道處理關卡,經過關卡都會被執行相應操作,最終完成來源資料到目的資料的操作。
過濾器是一種設計模式,在Java的各種架構及容器都有頻繁地使用以達到更好的擴充性和邏輯解耦。往下用一個例子看看過濾器如何工作。
① 輸入緩衝介面InputBuffer,提供讀取操作:
public interface InputBuffer {
public int doRead(byte[] chunk) throws IOException;
}
② 輸入過濾器介面InputFilter,繼承InputBuffer類,額外提供setBuffer方法設定前一個緩衝:
public interface InputFilter extends InputBuffer {
public void setBuffer(InputBuffer buffer);
}
③ 輸入緩衝裝置,類比通過請求體讀取器InputStreamInputBuffer從操作底層擷取請求體位元組數組,並且裡麵包含了若干個過濾器,緩衝裝置在執行讀取操作時會自動判斷是否有過濾器,如存在則將讀取後的位元組在經過層層過濾,得到最終的目的資料。
public class InternalInputBuffer implements InputBuffer {
boolean isEnd = false;
byte[] buf = new byte[4];
protected int lastActiveFilter = -1;
protected InputFilter[] activeFilters = new InputFilter[2];
InputBuffer inputStreamInputBuffer = (InputBuffer) new InputStreamInputBuffer();
public void addActiveFilter(InputFilter filter) {
if (lastActiveFilter == -1) {
filter.setBuffer(inputStreamInputBuffer);
} else {
for (int i = 0; i <= lastActiveFilter; i++) {
if (activeFilters[i] == filter)
return;
}
filter.setBuffer(activeFilters[lastActiveFilter]);
}
activeFilters[++lastActiveFilter] = filter;
}
public int doRead(byte[] chunk) throws IOException {
if (lastActiveFilter == -1)
return inputStreamInputBuffer.doRead(chunk);
else
return activeFilters[lastActiveFilter].doRead(chunk);
}
protected class InputStreamInputBuffer implements InputBuffer {
public int doRead(byte[] chunk) throws IOException {
if (isEnd == false) {
buf[0] = 'a';
buf[1] = 'b';
buf[2] = 'a';
buf[3] = 'd';
System.arraycopy(buf, 0, chunk, 0, 4);
isEnd = true;
return chunk.length;
} else {
return -1;
}
}
}
}
④ 清理過濾器ClearFilter,負責將讀取的位元組數組中的字元a換成f:
public class ClearFilter implements InputFilter {
protected InputBuffer buffer;
public int doRead(byte[] chunk) throws IOException {
int i = buffer.doRead(chunk);
if (i == -1)
return -1;
for (int j = 0; j < chunk.length; j++)
if (chunk[j] == 'a')
chunk[j] = 'f';
return i;
}
public InputBuffer getBuffer() {
return buffer;
}
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
}
}
⑤ 大寫過濾器UpperFilter,負責將讀取的位元組數組全部變成大寫:
public class UpperFilter implements InputFilter {
protected InputBuffer buffer;
public int doRead(byte[] chunk) throws IOException {
int i = buffer.doRead(chunk);
if (i == -1)
return -1;
for (int j = 0; j < chunk.length; j++)
chunk[j] = (byte) (chunk[j] - 'a' + 'A');
return i;
}
public InputBuffer getBuffer() {
return buffer;
}
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
}
}
⑥ 測試類別,建立輸入緩衝裝置,接著建立清理過濾器和大寫過濾器,把它們添加到輸入緩衝裝置,執行讀取操作,出來的就是經過兩個過濾器處理後的資料了,結果為“FBFD”,如果有其他處理需求通過實現InputFilter介面編寫過濾器並添加即可。
public class Test {
public static void main(String[] args) {
InternalInputBuffer internalInputBuffer = new InternalInputBuffer();
ClearFilter clearFilter = new ClearFilter();
UpperFilter upperFilter = new UpperFilter();
internalInputBuffer.addActiveFilter(clearFilter);
internalInputBuffer.addActiveFilter(upperFilter);
byte[] chunk = new byte[4];
try {
int i = 0;
while (i != -1) {
i = internalInputBuffer.doRead(chunk);
if (i == -1)
break;
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(new String(chunk));
}
}
由於篇幅問題,上面過程已經盡量類比描述tomcat輸入緩衝的工作流程,但實際使用的過濾器並非上面所述,主要包含四個過濾器:IdentityInputFilter、VoidInputFilter、BufferedInputFilter、ChunkedInputFilter。IdentityInputFilter過濾器在http包含content-length頭部並且指定的長度大於0時使用,它將根據指定的長度從底層讀取響應長度的位元組數組,當讀取足夠資料後將直接返回-1,避免再次執行底層操作;VoidInputFilter過濾器用於攔截讀取底層資料操作,當http不包含content-length頭部時說明沒有請求體,沒必要執行讀取通訊端底層操作,所以用這個過濾器攔截;BufferedInputFilter過濾器負責讀取請求體並將其緩衝起來,後面讀取請求體時直接從此緩衝區讀取;ChunkedInputFilter過濾器專門用於處理分塊傳輸,分塊傳輸是一種資料轉送機制,當沒有指定content-length時可通過分塊傳輸完成通訊。
以上就是tomcat的通訊端緩衝裝置的過濾器的機制及其實現方法,並且簡單介紹了tomcat中不同的過濾器的功能,過濾器模式讓tomcat在後期程式擴充升級變得更容易。
喜歡研究java的同學可以交個朋友,下面是本人的號: