SpringBoot基於註解方式配置Filter

笨鳥先飛的菜鳥發表於2020-12-26

Filter在Web應用中是一個很常見的元件,在Spring應用中Filter的建立方式有最簡單和常用的方式有兩種(可能還有其他,若有其他優秀的建立方式,歡迎交流學習):

  • 實現javax.servlet.Filter介面
  • 繼承org.springframework.web.filter.OncePerRequestFilter類

在傳統Web應用中,每個Web應用都有一個web.xml檔案,方便我們配置各種Servlet、Filter、Listener等。在SpringBoot應用中大多推崇零XML配置的方式,所以在沒有web.xml的情況下如何配置Filter呢?這裡先介紹使用@WebFilter註解配置的方式。

  • 採用繼承org.springframework.web.filter.OncePerRequestFilter的方式建立一個Filter
package com.jackson.spring.boot.filters;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = {"/*"})
@Order(value = 2)
@Slf4j
public class UrlFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        logger.info("urlFilter .....");
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
  • 使用實現javax.servlet.Filter建立一個Filter
package com.jackson.spring.boot.filters;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = {"/*"},
        initParams = {@WebInitParam(name = "fileTypes", value = "doc;xls;zip")})
@Order(value = 3)
@Slf4j
public class ParamFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("---------" + filterConfig.getInitParameter("fileTypes"));
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("ParamFilter.doFilter ...");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

在程式碼中我們可以看到使用@WebFilter註解去宣告一個Filter在一個Web應用中。其官方描述如下:

The @WebFilter  annotation is used to declare a filter in a web application. The annotated class must extend the javax.servlet.Filter interface

我們知道過濾器的執行順序是按照web.xml中是配置順序決定的,那麼在沒有web.xml檔案的時候,如何指定Filter的執行順序呢?程式碼中@Order註解就是指定過濾器的執行順序的。

使用上述配置啟動後訪問,過濾器可按照預期的執行順序執行,但是有一個問題,細心的朋友就會發現上述例子中的ParamFilter在執行init()方法的時候,日誌列印獲取init-param引數時為空。這是為何?我們對於的initParam引數配置如下:

initParams = {@WebInitParam(name = "fileTypes", value = "doc;xls;zip")})

翻了好久的資料,在這篇部落格中得到解決:https://www.liangzl.com/get-article-detail-129981.html 。在使用@WebServlet、@WebFilter、@WebListener、@WebInitParam等servlet註解時需要在SpringBoot的啟動類上新增@ServletComponentScan註解,否則不會生效。這裡我試了一下不使用@ServletComponentScan註解而使用@Component註解情況下發現@WebInitParam註解會失效,大家也可以嘗試一下。為了安全起見還是把@ServletComponentScan註解加到啟動類上(注意沒有刪除@Component註解)啟動的時候發現同一個Filter被載入了兩個到Spring容器中。若配置了filterName屬性,還有可能出現如下提示資訊,啟動失敗:

A bean with that name has already been defined

這裡其實很容易就可以知道是@ServletComponentScan和@Component註解都建立了一個物件到Spring容器中,為了可以使用Servlet的特性,所以推薦使用@ServletComponentScan註解。最終其中一個Filter的配置修改為如下配置:

package com.jackson.spring.boot.filters;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;


@WebFilter(urlPatterns = {"/*"}, filterName = "paramFilter",
        initParams = {@WebInitParam(name = "fileTypes", value = "doc;xls;zip")})
@Order(value = 3)
@Slf4j
public class ParamFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("---------" + filterConfig.getInitParameter("fileTypes"));
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("ParamFilter.doFilter ...");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

啟動類的配置為:

package com.jackson.spring.boot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication(scanBasePackages = {"com.jackson.spring.boot"})
@ServletComponentScan
public class Application {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);
    public static void main( String[] args ) {
        logger.info("application starting ....");
        SpringApplication.run(Application.class);
    }
}

 

 

相關文章