Filter

Filter

传统 JavaWeb

新建过滤器

引入 tomcat-jarservlet-api.jar,新建过滤器如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.master;

import javax.servlet.*;
import java.io.IOException;

/**
* .
*
* @author heng.wang
* @since 2022/01/04 0004 19:51
*/
public class LoginLimitFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("过滤器初始化");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器执行");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}

配置过滤器

添加到web.xml

1
2
3
4
5
6
7
8
<filter>
<filter-name>loginLimitFilter</filter-name>
<filter-class>cn.master.LoginLimitFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginLimitFilter</filter-name>
<url-pattern>/login</url-pattern>
</filter-mapping>

这样就可以生效了,默认拦截/login的请求。

springboot

第一种方式(注解方式)

新建过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.slf4j.LoggerFactory;

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


@WebFilter(filterName = "loginLimitFilter", urlPatterns = {"/login"})
public class LoginLimitFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(LoginLimitFilter.class);


@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.debug("过滤器初始化");
Filter.super.init(filterConfig);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.debug("过滤器执行");
chain.doFilter(request, response);
}

@Override
public void destroy() {
log.debug("过滤器销毁");
}
}

配置过滤器

添加 servlet 组件扫描注解@ServletComponentScan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.springboottest;

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

@SpringBootApplication
@ServletComponentScan
public class SpringbootTestApplication {

public static void main(String[] args) {
SpringApplication.run(SpringbootTestApplication.class, args);
}

}

第二种方式(注册 Bean)

新建过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.springboottest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.FilterConfig;
import javax.servlet.*;
import java.io.IOException;

public class LoginLimitFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(LoginLimitFilter.class);


@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.debug("过滤器初始化");
Filter.super.init(filterConfig);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.debug("过滤器执行");
chain.doFilter(request, response);
}

@Override
public void destroy() {
log.debug("过滤器销毁");
}
}

其实就是去掉(注解模式的注解)

配置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.springboottest;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginLimitFilter> corsFilterRegistration() {
FilterRegistrationBean<LoginLimitFilter> filterRegistrationBean =
new FilterRegistrationBean<>(loginLimitfilter());
filterRegistrationBean.setUrlPatterns(Collections.singleton("/login"));
filterRegistrationBean.setOrder(100);
return filterRegistrationBean;
}

@Bean
public LoginLimitFilter loginLimitfilter() {
return new LoginLimitFilter();
}
}

两种区别

传统方式

可以正常使用并且根据配置的 url 进行拦截,没啥说的

springboot

注解方式

  1. 注解方式的过滤器需要搭配@ServletComponentScan一起用过滤器才会生效。
  2. 这种方式注解里的urlPatterns = {"/login"}不会生效,只会全局拦截。

bean 方式

  1. 需要将过滤器注册成 bean
  2. 再将 bean 注册到 FilterRegistrationBean<T>

此时过滤器以 bean 的方式注册到容器内

  1. 支持注入其他 bean
  2. 支持排序
  3. url 匹配生效

推荐方式

推荐将过滤器作为 bean 交给 spring 容易管理,因为大多数情况下会用到其他的 bean 来进行操作,除非说只记录个日志那么确实这种情况不需要注册到 spring 容器。

既然交给容器去管理 不妨试试另一种写法

新建过滤器

不直接实现Filter而是继承OncePerRequestFilter,该类继承GenericFilterBean并且实现了Filter, BeanNameAware, EnvironmentAware, EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.springboottest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;

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

public class LoginLimitFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(LoginLimitFilter.class);


@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.debug("过滤器执行");
filterChain.doFilter(request, response);
}

@Override
protected void initFilterBean() throws ServletException {
log.debug("初始化过滤器需要的bean");
super.initFilterBean();
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
log.debug("check if do filer");
return super.shouldNotFilter(request);
}
}

配置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.springboottest;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginLimitFilter> corsFilterRegistration() {
FilterRegistrationBean<LoginLimitFilter> filterRegistrationBean =
new FilterRegistrationBean<>(new LoginLimitFilter());
filterRegistrationBean.setUrlPatterns(Collections.singleton("/login"));
filterRegistrationBean.setOrder(100);
return filterRegistrationBean;
}

// @Bean
// public LoginLimitFilter loginLimitfilter() {
// return new LoginLimitFilter();
// }
}

取消注入 bean,并在new FilterRegistrationBean<>(new LoginLimitFilter()); 手动 new 一个 filter

解释

因为如果此时还以 bean 的方式注入的话会在启动时时候注册两次。

第一次是本身作为 bean 会初始化

第二次是org.springframework.web.filter.GenericFilterBean#initFilterBean会初始化一次,因为这种方式试讲 filter 作为 bean,所以会注入一次,也就是说,这个 filter 会自己注入到容器里面,不需要手动注入。

所以 如果需要其他用到 bean 重写这个方法即可(例如构造器注入),容器会自动注入。

启动日志

可选注入

已经作为 bean 了那就可以配合@ConditionalOnProperty,更加灵活。

作者

Heng.Wang

发布于

2022-01-04

更新于

2023-09-20

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×