Springboot項(xiàng)目快速實(shí)現(xiàn)過(guò)濾器功能
前言
很多時(shí)候,當(dāng)你以為掌握了事實(shí)真相的時(shí)間,如果你能再深入一點(diǎn),你可能會(huì)發(fā)現(xiàn)另外一些真相。比如面向切面編程的最佳編程實(shí)踐是AOP,AOP的主要作用就是可以定義切入點(diǎn),并在切入點(diǎn)縱向織入一些額外的統(tǒng)一操作,避免與業(yè)務(wù)代碼過(guò)度耦合。熟悉java web項(xiàng)目的都知道,另外還有過(guò)濾器(filter)、攔截器(interceptor)也有類似AOP的功能特性,那么問(wèn)題來(lái)了:為什么一般說(shuō)面向切面編程就是指AOP,而不是過(guò)濾器和攔截器?過(guò)濾器和攔截器在Spring boot中怎么實(shí)現(xiàn)?這三者之間有什么區(qū)別?Springboot項(xiàng)目快速實(shí)現(xiàn)Aop功能中分享了AOP的相關(guān)實(shí)現(xiàn),下面將再用兩到三篇文章,分別和大家分享一下過(guò)濾器、攔截器的實(shí)現(xiàn),以及AOP、過(guò)濾器、攔截器之間的橫向?qū)Ρ?,以便在業(yè)務(wù)開(kāi)發(fā)中,能夠快速、正確選對(duì)具體實(shí)現(xiàn)方法。
環(huán)境配置
jdk版本:1.8
開(kāi)發(fā)工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE
Filter簡(jiǎn)介
Filter, 中文意思是過(guò)濾器,F(xiàn)ilter的全限定類名是javax.servlet.Filter,可以看出這是與servelt相關(guān)的一個(gè)接口;SpringMVC核心是DispatcherServlet,而DispatcherServlet又繼承了Servlet,進(jìn)而可以推測(cè)出Filter與SpringMVC也是關(guān)聯(lián)關(guān)系。

事實(shí)上這樣的推測(cè)也是正確的,在SpringMVC項(xiàng)目中,filter在瀏覽器與服務(wù)器之間起過(guò)濾的作用,可以截取客戶端和服務(wù)端之間的請(qǐng)求和響應(yīng)信息,并根據(jù)這些請(qǐng)求-響應(yīng)信息作一些其他的操作,但是要注意,filter并不能改變請(qǐng)求-響應(yīng)信息;
核心類
Filter
Filter接口的全限定類名是javax.servlet.Filter,該接口有三個(gè)方法,分別是
1、init(...):用于初始化Filter;
2、doFilter(...):過(guò)濾請(qǐng)求和攔截響應(yīng)信息的具體實(shí)現(xiàn)在這個(gè)方法里;
3、destroy(...):Filter對(duì)象被銷毀時(shí)觸發(fā),主要用于做一些收尾工作,如資源的釋放等;
FilterConfig
FilterConfig接口的全限定類名是javax.servlet.FilterConfig,該接口主要有四個(gè)方法,分別是:
1、getFilterName() 獲取Filter的名字;
2、getServletContext() 獲取ServletContext對(duì)象(即application);
3、getInitParameter() 獲取Filter的初始化參數(shù);
4、getInitParameterNames() 獲取所有初始化參數(shù)的名字;
FilterChain
FilterChainr接口的全限定類名是javax.servlet.FilterChain,該接口只有一個(gè)方法,是doFilter()方法,用于調(diào)用Filter鏈上的下一個(gè)過(guò)濾器,如果當(dāng)前過(guò)濾器為最后一個(gè)或只有一個(gè)過(guò)濾器,則該過(guò)濾器則將請(qǐng)求發(fā)送到目標(biāo)資源。
MyFilter2是自己實(shí)現(xiàn)的過(guò)濾器,實(shí)現(xiàn)了Filter接口;Filter接口依賴FilterChain接口和FilterConfig接口,其中FilterChain接口的實(shí)現(xiàn)類是org.apache.catalina.core.ApplicationFilterChain,F(xiàn)ilterConfig接口的實(shí)現(xiàn)類是org.apache.catalina.core.ApplicationFilterConfig;

工作原理
1、項(xiàng)目啟動(dòng)的時(shí)候,先執(zhí)行Filter的構(gòu)造方法,完成相關(guān)Filter對(duì)象的注冊(cè);
2、緊接著,F(xiàn)ilter對(duì)象的init()方法被調(diào)用,開(kāi)始對(duì)Filter做一些初始化操作;
3、項(xiàng)目啟動(dòng)完成后,客戶端每次向服務(wù)端發(fā)起請(qǐng)求時(shí),如果請(qǐng)求地址與過(guò)濾器定義的地址匹配,則會(huì)執(zhí)行Filter的doFilter();如果匹配上多個(gè)過(guò)濾器,則會(huì)形成一個(gè)鏈路,依次調(diào)用各個(gè)過(guò)濾器對(duì)象的doFilter();服務(wù)端作出響應(yīng)后,也會(huì)再次執(zhí)行到各個(gè)過(guò)濾器對(duì)象的doFilter();請(qǐng)求和響應(yīng)時(shí),過(guò)濾器鏈的執(zhí)行順序是先進(jìn)后出;
4、服務(wù)器停止時(shí)調(diào)用Filter的destroy()方法,用來(lái)釋放資源。

實(shí)現(xiàn)方式
Springboot項(xiàng)目中一般有兩種方式:
1、@WebFilter注解,即javax.servlet.annotation.WebFilter;
2、FilterRegistrationBean,即org.springframework.boot.web.servlet.FilterRegistrationBean;
兩種方式,都需要在啟動(dòng)類上增加注解@ServletComponentScan,用于開(kāi)啟servlet相關(guān)bean的掃描,其中包含有過(guò)濾器(Filter);
@SpringBootApplication
@ServletComponentScan
public class FanfuApplication {
public static void main(String[] args) {
SpringApplication.run(FanfuApplication.class, args);
}
}代碼實(shí)現(xiàn)
1、WebFilter注解里,定義一下過(guò)濾器的名字,以及要對(duì)哪些請(qǐng)求進(jìn)行過(guò)濾,“/*”表示對(duì)所有的請(qǐng)求都過(guò)濾,在實(shí)際業(yè)務(wù)中,可具體對(duì)待;如果在初始化的時(shí)候,需要攜帶一些初始化的參數(shù),可以在initParams屬性上,使用@WebInitParam注解來(lái)定義初始化參數(shù)名稱和具體的值,這些參數(shù)可以在filter對(duì)象初始化的時(shí)候獲取到;MyFIlter1和MyFIlter2使用的注解方式定義的過(guò)濾器;
@Slf4j
@WebFilter(filterName = "myFilter1", urlPatterns = "/*", initParams = {@WebInitParam(name = "creator", value = "fanfu")})
public class MyFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("http://myFilter1初始化開(kāi)始");
String creator = filterConfig.getInitParameter("creator");
log.info("http://初始化參數(shù)creator:{}",creator);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("http://myFilter1開(kāi)始執(zhí)行");
chain.doFilter(request, response);
log.info("http://myFilter1結(jié)束執(zhí)行");
}
@Override
public void destroy() {
log.info("http://myfilter1被銷毀");
}
}@Slf4j
@WebFilter(filterName = "myFilter2", urlPatterns = "/*")
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("http://myFilter2初始化開(kāi)始");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("http://myFilter2開(kāi)始執(zhí)行");
chain.doFilter(request, response);
log.info("http://myFilter2結(jié)束執(zhí)行");
}
@Override
public void destroy() {
log.info("http://myFilter2被銷毀");
}
}2、FilterRegistrationBean方式
在Springboot項(xiàng)目的配置類中,使用FilterRegistrationBean來(lái)包裝自定義的過(guò)濾器,這種方式的最大好處就是可以自定義過(guò)濾器的執(zhí)行順序,數(shù)字越小,執(zhí)行時(shí)的優(yōu)先級(jí)就越高;MyFIlter3和MyFIlter4是使用FilterRegistrationBean方式定義的過(guò)濾器;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean filterRegistration1() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MyFilter3());
filterRegistrationBean.addUrlPatterns("/*");//定義過(guò)濾器對(duì)哪些請(qǐng)求路徑進(jìn)行過(guò)濾,/*表示對(duì)所有請(qǐng)求都過(guò)濾
filterRegistrationBean.setOrder(2);//定義過(guò)濾器的執(zhí)行優(yōu)先級(jí),數(shù)據(jù)越小優(yōu)先級(jí)越高
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistration2() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MyFilter4());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
}@Slf4j
public class MyFilter3 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("http://MyFilter3初始化開(kāi)始");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("http://MyFilter3開(kāi)始執(zhí)行");
chain.doFilter(request,response);
log.info("http://MyFilter3結(jié)束執(zhí)行");
}
@Override
public void destroy() {
log.info("http://MyFilter3被銷毀");
}
}@Slf4j
public class MyFilter4 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("http://MyFilter4初始化開(kāi)始");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("http://MyFilter4開(kāi)始執(zhí)行");
chain.doFilter(request,response);
log.info("http://MyFilter4結(jié)束執(zhí)行");
}
@Override
public void destroy() {
log.info("http://MyFilter4被銷毀");
}
}未定義myFilter3、myFilter4的執(zhí)行優(yōu)先級(jí),即采取自然排序時(shí)的執(zhí)行結(jié)果:在請(qǐng)求前myFilter3的執(zhí)行時(shí)機(jī)早于myFilter4,響應(yīng)后myFilter3的執(zhí)行時(shí)機(jī)要晚于myFilter4;

定義myFilter4的優(yōu)先級(jí)高于myFilter3時(shí),執(zhí)行結(jié)果:在請(qǐng)求前myFilter4的執(zhí)行時(shí)機(jī)早于myFilter3,響應(yīng)后的myFilter4的執(zhí)行時(shí)機(jī)要晚于myFilter3;

總結(jié)
過(guò)濾器的實(shí)現(xiàn)是比較簡(jiǎn)單,通過(guò)梳理這個(gè)過(guò)程,我get到以下幾個(gè)點(diǎn):
1、過(guò)濾器是用于SpringMVC項(xiàng)目中,即與servlet相關(guān)的項(xiàng)目;
2、過(guò)濾器的執(zhí)行時(shí)機(jī)是在請(qǐng)求前和響應(yīng)后,有兩種實(shí)現(xiàn)方式,即@WebFilter注解和FilterRegistrationBean;如果對(duì)過(guò)濾器的執(zhí)行順序沒(méi)有限制要求,則可以使用第一種;如果對(duì)過(guò)濾器的執(zhí)行順序有明確限制,則可以使用第二種;
3、如果有多個(gè)過(guò)濾器對(duì)象時(shí),會(huì)形成一個(gè)過(guò)濾器鏈,過(guò)濾器的執(zhí)行順序是先進(jìn)后出;
4、過(guò)濾器可以過(guò)濾請(qǐng)求和攔截響應(yīng),但是不能改變請(qǐng)求值和響應(yīng)值;
到此這篇關(guān)于Springboot項(xiàng)目快速實(shí)現(xiàn)過(guò)濾器功能的文章就介紹到這了,更多相關(guān)Springboot實(shí)現(xiàn)過(guò)濾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解SpringBoot 解決攔截器注入Service為空問(wèn)題
這篇文章主要介紹了詳解SpringBoot 解決攔截器注入Service為空問(wèn)題的解決,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
springboot themaleaf 第一次進(jìn)頁(yè)面不加載css的問(wèn)題
這篇文章主要介紹了springboot themaleaf 第一次進(jìn)頁(yè)面不加載css的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Java手動(dòng)實(shí)現(xiàn)Redis的LRU緩存機(jī)制
在Java中LRU的實(shí)現(xiàn)方式是使用HashMap結(jié)合雙向鏈表,HashMap的值是雙向鏈表的節(jié)點(diǎn),雙向鏈表的節(jié)點(diǎn)也保存一份key value。2021-05-05
Java超詳細(xì)教你寫一個(gè)斗地主洗牌發(fā)牌系統(tǒng)
這篇文章主要介紹了怎么用Java來(lái)你寫一個(gè)斗地主種洗牌和發(fā)牌的功能,斗地主相信大家都知道,同時(shí)也知道每一局都要洗牌打亂順序再發(fā)牌,本篇我們就來(lái)實(shí)現(xiàn)這個(gè)功能,感興趣的朋友跟隨文章往下看看吧2022-03-03
SpringBoot AOP AspectJ切面技術(shù)介紹與實(shí)現(xiàn)方式
這篇文章主要介紹了Springboot如何使用Aspectj實(shí)現(xiàn)AOP面向切面編程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10

