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

Filter過濾器

Java Filter詳解

本篇將主要集中在fitler的以下幾個(gè)知識(shí)點(diǎn):

干嘛的

怎么用

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

注意事項(xiàng)

基本知識(shí)

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

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

一個(gè)http請(qǐng)求過來之后

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

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

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

通過上面的流程,可以推算使用場(chǎng)景:

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

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

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

1. 基本使用姿勢(shì)

要使用一個(gè)Filter,一半需要兩步,實(shí)現(xiàn)Filter接口的自定義類,web.xml中對(duì)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();
}

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

詳細(xì)case后面再說

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

<!-- 解決亂碼的問題 -->
<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>

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

實(shí)例

我們的實(shí)例,就拿大名鼎鼎的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");
	}
}

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

filterChain.doFilter(request, response);

注意

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

疑問

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

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

具體的實(shí)現(xiàn)邏輯是在 org.springframework.web.filter.GenericFilterBean#init 中,同樣是Spring實(shí)現(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");
		}
}

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

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

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

先實(shí)現(xiàn)一個(gè)最最最簡(jiǎ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ù)時(shí),最好不要直接使用獲取請(qǐng)求流的方式,如果獲取請(qǐng)求流,那么Servlet就獲取不到請(qǐng)求參數(shù)了

問題三:多個(gè)filter的順序怎么定

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

精確匹配 > 最長(zhǎng)匹配 > 其他模糊匹配 > 沒有匹配的則是404

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

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

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

測(cè)試case如下,我們定義三個(gè)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)入下一個(gè)Filter或者Servlet執(zhí)行邏輯,當(dāng)執(zhí)行完成之后,依然會(huì)回到Filter這一層,繼續(xù)走下去

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

用戶信息獲取,身份校驗(yàn)

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

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

Filter執(zhí)行順序

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

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

Filter過濾器的注意事項(xiàng)

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

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

全部教程
主站蜘蛛池模板: 在线观看91精品国产不卡免费 | 精品免费国产一区二区三区 | 99热久久精品首页 | jizzz亚洲美女 | 日韩亚洲综合精品国产 | 九九热视频免费在线观看 | 国产一区中文字幕 | 国产精品99久久久久久人 | 久久精品这里精品 | 色综合久久久 | 久久精品免费大片国产大片 | 久久精品视频亚洲 | 日本爱情动作片网址 | 九九视频高清视频免费观看 | 伊人久久综合影院首页 | 久久一 | 久久美剧免费在线观看 | 又黄又爽又色的免费毛片 | 久久新网址 | 日韩亚洲视频 | 国产成人精品久久二区二区 | 亚洲欧美精品中文字幕 | 精品国产免费观看一区 | 神马我我不卡伦影视 | 亚洲天堂一区 | 久久久久99| 亚洲精品另类 | 亚洲欧美日本国产综合在线 | 玖玖在线免费视频 | 天天操天天插天天干 | 91精品久久久久含羞草 | 69毛片 | 国产男女在线观看 | 色爽女| 成 人 黄 色 大 片 | 精品国产91久久久久久久a | 四虎国产成人免费观看 | 精品四虎免费观看国产高清午夜 | 一久久| 精品福利国产 | 9热在线精品视频观看 |