传统 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;
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
注解方式
- 注解方式的过滤器需要搭配
@ServletComponentScan
一起用过滤器才会生效。
- 这种方式注解里的
urlPatterns = {"/login"}
不会生效,只会全局拦截。
bean 方式
- 需要将过滤器注册成 bean
- 再将 bean 注册到 FilterRegistrationBean<T>
此时过滤器以 bean 的方式注册到容器内
- 支持注入其他 bean
- 支持排序
- 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,并在new FilterRegistrationBean<>(new LoginLimitFilter());
手动 new 一个 filter
解释
因为如果此时还以 bean 的方式注入的话会在启动时时候注册两次。
第一次是本身作为 bean 会初始化
第二次是org.springframework.web.filter.GenericFilterBean#initFilterBean
会初始化一次,因为这种方式试讲 filter 作为 bean,所以会注入一次,也就是说,这个 filter 会自己注入到容器里面,不需要手动注入。
所以 如果需要其他用到 bean 重写这个方法即可(例如构造器注入),容器会自动注入。
可选注入
已经作为 bean 了那就可以配合@ConditionalOnProperty
,更加灵活。