大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

Filter過濾器

Java Filter詳解

本篇將主要集中在fitler的以下幾個知識點:

干嘛的

怎么用

多個Filter執(zhí)行的先后順序

注意事項

基本知識

Filter稱之為過濾器,是用來做一些攔截的任務(wù), 在Servlet接受請求之前,做一些事情,如果不滿足限定,可以拒絕進(jìn)入Servlet

從上面的圖,可以看出一個Filter的工作流程:

一個http請求過來之后

首先進(jìn)入filter,執(zhí)行相關(guān)業(yè)務(wù)邏輯

若判定通行,則進(jìn)入Servlet邏輯,Servlet執(zhí)行完畢之后,又返回Filter,最后在返回給請求方

判定失敗,直接返回,不需要將請求發(fā)給Servlet

通過上面的流程,可以推算使用場景:

在filter層,來獲取用戶的身份

可以考慮在filter層做一些常規(guī)的校驗(如參數(shù)校驗,referer校驗等)

可以在filter層做穩(wěn)定性相關(guān)的工作(如全鏈路打點,可以在filter層分配一個traceId;也可以在這一層做限流等)

1. 基本使用姿勢

要使用一個Filter,一半需要兩步,實現(xiàn)Filter接口的自定義類,web.xml中對filter的定義

public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;
	
	
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;
 
 
    public void destroy();
}

主要就三個方法,從命名來看,也比較清晰,在創(chuàng)建Filter對象的時候,調(diào)用 init 方法,銷毀Filter對象的時候,調(diào)用 destroy 方法,當(dāng)請求過來之后,調(diào)用 doFilter,也就是主要的業(yè)務(wù)邏輯所在了

詳細(xì)case后面再說

接下來就是xml的配置了,和Servlet類似,每自定義一個,都需要在xml中加上一個配置(挺繁瑣的操作的)

<!-- 解決亂碼的問題 -->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

配置也比較簡單了,一個 一個 前者定義具體的Filter,后者表示這個Filter攔截的URL (看起來和Servlet的配置規(guī)則沒什么兩樣)

實例

我們的實例,就拿大名鼎鼎的CharacterEncodingFilter來說明,順帶膜拜下Spring的大神的優(yōu)秀源碼

public class CharacterEncodingFilter extends OncePerRequestFilter {
 
	private String encoding;
 
	private boolean forceEncoding = false;
 
	public CharacterEncodingFilter() {
	}
 
	public CharacterEncodingFilter(String encoding) {
		this(encoding, false);
	}
 
	public CharacterEncodingFilter(String encoding, boolean forceEncoding) {
		Assert.hasLength(encoding, "Encoding must not be empty");
		this.encoding = encoding;
		this.forceEncoding = forceEncoding;
	}
 
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}
 
	public void setForceEncoding(boolean forceEncoding) {
		this.forceEncoding = forceEncoding;
	}
 
	@Override
	protected void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
 
		if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
			request.setCharacterEncoding(this.encoding);
			if (this.forceEncoding) {
				response.setCharacterEncoding(this.encoding);
			}
		}
		filterChain.doFilter(request, response);
		System.out.printl("servelt 執(zhí)行完成,又返回filter");
	}
}

上面的實現(xiàn)比較簡單,主要將視線集中在 doFilterInternal 方法內(nèi)部,如果要設(shè)置編碼參數(shù),則直接修改 HttpServletRequest, HttpServletResponse 兩個參數(shù),操作完成之后,執(zhí)行下面這一行

filterChain.doFilter(request, response);

注意

上面這一行執(zhí)行,表示Filter層已經(jīng)通過了,請求可以轉(zhuǎn)發(fā)給下一個Filter或者直接傳給Servlet,而下一個Filter, Servlet執(zhí)行完成之后,還會繼續(xù)往下走,就是上面的那一行輸出,也會被調(diào)用(那一行是我加的,源碼中沒有),所以,如果你不希望繼續(xù)往下走,那么就簡單了,不執(zhí)行上面的那一行即可

疑問

問題一:看了上面的源碼,一個很明顯的問題就是,參數(shù)怎么設(shè)置的?

仔細(xì)看上面的源碼,發(fā)現(xiàn)自定義Filter是繼承 org.springframework.web.filter.OncePerRequestFilter 而不是直接實現(xiàn)的 Filter 接口,而且方法內(nèi)也沒有顯示的實現(xiàn) init()方法,所有很容易猜到是父類中實現(xiàn)了參數(shù)的初始化過程

具體的實現(xiàn)邏輯是在 org.springframework.web.filter.GenericFilterBean#init 中,同樣是Spring實現(xiàn)的,主要代碼撈出來


public final void init(FilterConfig filterConfig) throws ServletException {
		Assert.notNull(filterConfig, "FilterConfig must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
		}
 
		this.filterConfig = filterConfig;
 
		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			String msg = "Failed to set bean properties on filter '" +
				filterConfig.getFilterName() + "': " + ex.getMessage();
			logger.error(msg, ex);
			throw new NestedServletException(msg, ex);
		}
 
		// Let subclasses do whatever initialization they like.
		initFilterBean();
 
		if (logger.isDebugEnabled()) {
			logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
		}
}

看上面一大串的代碼,到底干了嘛? 簡單來講,就是獲取xml中配置的參數(shù),然后填充到Filter對象中(對Srping而言,CharacterEncodingFilter就是一個bean),這個具體的邏輯和本篇關(guān)系不大,就直接跳過了

問題二:在Filter層中可以獲取參數(shù)么

從doFilter的方法簽名中看,既然有Request參數(shù),那么應(yīng)該是可以獲取到請求參數(shù)的,那么實際驗證一下

先實現(xiàn)一個最最最簡單的Filter


public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
        System.out.println("in filter");
        System.out.println("args: " + JSON.toJSONString(request.getParameterMap()));
        chain.doFilter(request, response);
        System.out.println("out filter");
    }
 
    @Override
    public void destroy() {
    }
}

輸出如下

in filter
args: {"name":["Hello"],"password":["world"]}
out filter

在Filter中獲取參數(shù)時,最好不要直接使用獲取請求流的方式,如果獲取請求流,那么Servlet就獲取不到請求參數(shù)了

問題三:多個filter的順序怎么定

前面學(xué)習(xí)Servlet的時候,也有這個問題,一個URL被多個Servlet命中了,那么先后順序是怎樣的呢?

精確匹配 > 最長匹配 > 其他模糊匹配 > 沒有匹配的則是404

那么Filter呢,他們的區(qū)別還是比較明顯的,很多Filter都是攔截所有的請求,即很多Filter的命中規(guī)則都是一樣的,那么怎么辦?

先執(zhí)行帶有url-pattern標(biāo)簽的filter,再執(zhí)行帶有servlet-name標(biāo)簽的filter

如果同為url-pattern或servlet-name,則會按照在web.xml中的聲明順序執(zhí)行

測試case如下,我們定義三個Filter:

TestFilter: 匹配所有路徑

ATestFilter: 匹配所有路徑

ServletFilter: 匹配 mvc-servlet

// ATestFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("in ATestFilter");
    chain.doFilter(request, response);
}
 
 
// TestFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("in TestFilter");
    chain.doFilter(request, response);
}
 
// ServletFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("in ServletFilter");
    chain.doFilter(request, response);
}
<filter>
    <filter-name>servletFilter</filter-name>
    <filter-class>com.test.ServletFilter</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>servletFilter</filter-name>
    <servlet-name>mvc-dispatcher</servlet-name>
</filter-mapping>
 
<filter>
    <filter-name>testFilter</filter-name>
    <filter-class>com.test.TestFilter</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>testFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
 
 
<filter>
    <filter-name>atestFilter</filter-name>
    <filter-class>com.test.ATestFilter</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>atestFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

輸出結(jié)果


in TestFilter
in ATestFilter
in ServletFilter

小結(jié)

Filter 通常用于JavaWeb的過濾使用,通過doFilter方法中執(zhí)行

chain.doFilter(request, response);,進(jìn)入下一個Filter或者Servlet執(zhí)行邏輯,當(dāng)執(zhí)行完成之后,依然會回到Filter這一層,繼續(xù)走下去

針對上面的邏輯,F(xiàn)ilter的常見應(yīng)用場景有:

用戶信息獲取,身份校驗

安全校驗(referer校驗失敗,直接拒絕)

穩(wěn)定性相關(guān)(限流,監(jiān)控埋點,全鏈路日志埋點)

Filter執(zhí)行順序

url-mapping 的優(yōu)先執(zhí)行,其次是 servlet-mapping

同一個匹配方式(如都是url-mapping)中,根據(jù)在xml中定義的先后順序來確定

Filter過濾器的注意事項

正常業(yè)務(wù),請記得一定執(zhí)行 chain.doFilter(request, response), 最后把它放在finnal塊中,防止你在Filter中的代碼拋異常導(dǎo)致進(jìn)入不到后續(xù)的邏輯

在Filter中不要直接獲取請求數(shù)據(jù)流(請求流被讀取完之后,Servlet就get不到了!)

全部教程
主站蜘蛛池模板: 国产精品四虎在线观看免费 | 国产精品久久久久鬼色 | 成人区精品一区二区毛片不卡 | 国产a视频| 日韩欧美国产中文 | 国内精品视频在线观看 | 亚洲香蕉国产高清在线播放 | 永久国产 | 97精品国产手机 | 全黄h全肉边做边吃奶在线观看 | 日韩区欧美区 | 四虎影视1515hh四虎免费 | 久久久久免费视频 | 在线观看av片永久免费 | 国产日韩中文字幕 | 亚洲欧美日韩在线不卡中文 | 另类婷婷 | 日韩精品一区二区三区中文3d | 超清波多野结衣精品一区 | 奇米影视7777久久精品人人爽 | 九九热精品在线视频 | 天天做天天看夜夜爽毛片 | 99视频久久精品久久 | 99精品视频在线 | 天天干天天色综合网 | 免费观看男女羞羞的视频网站 | 91九色首页 | 日日射天天操 | 久久天天躁综合夜夜黑人鲁色 | 中文一级国产特级毛片视频 | 97国内精品久久久久久久影视 | 成人精品一区二区久久 | 黄色a级毛片 | 顶级欧美色妇xxxxbbbb | 国产成人在线视频观看 | 朴妮唛禁福利视频在线 | 一本一本久久a久久综合精品蜜桃 | 久久精品这里是免费国产 | 亚洲精品第一综合99久久 | 欧美一区二区三区高清视频 | 国产成人禁片在线观看 |