前言

在 SpringMVC - 对于如何配置 Filter 的深度剖析 这篇文章中,我们知道了在 SpringMVC 环境中如何配置 Filter,接下来我们看一下如何在 SpringBoot 中配置 Filter

配置

1、使用原生注解

  • 首先定义一个 Filter 类,匹配 /hello 请求:
@WebFilter(filterName = "myFilter", urlPatterns = "/hello")
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("初始化我的过滤器");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("我的过滤器");filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {}
}
  • 使用 @ServletComponentScan 注解扫描原生组件
@SpringBootApplication
@ServletComponentScan(basePackages = "com.example.springboot.review.filter")
public class SpringBootReviewApplication {public static void main(String[] args) {SpringApplication.run(SpringBootReviewApplication.class, args);}}

2、使用 SpringBoot 提供的 FilterRegistrationBean

  • 定义一个 LoggerFilter
public class LoggerFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init LoggerFilter");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("LoggerFilter before doFilter");chain.doFilter(request, response);System.out.println("LoggerFilter after doFilter");}@Overridepublic void destroy() {}
}
  • 在配置类中使用 FilterRegistrationBean 注册 Filter
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<LoggerFilter> filterRegistrationBean() {FilterRegistrationBean<LoggerFilter> bean = new FilterRegistrationBean<>();bean.setFilter(new LoggerFilter());  // 这里可以使用 new,也可以在 Filter 上加 @Component 注入进来bean.addUrlPatterns("/hello");bean.setName("loggerFilter");bean.setOrder(1);    // 值越小,优先级越高return bean;}// 可以写多个 FilterRegistrationBean
}

3、直接在 Filter 上使用 @Component 注解

注意:这种方式默认会过滤所有的请求

@Component
@Order(-1)    // 可以指定优先级,不填的话默认为最小的优先级
public class HelloFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init HelloFilter");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("HelloFilter doFilter");chain.doFilter(request, response);}@Overridepublic void destroy() {Filter.super.destroy();}
}

或者:

@Configuration
public class FilterConfig {@Beanpublic Filter filter() {return (request, response, chain) -> {System.out.println("innerFilter doFilter");chain.doFilter(request, response);};}
}

4、使用 DelegatingFilterProxyRegistrationBean 注册已经注册为 BeanFilter

在上面 3 中,我们给 Filter 类上加了 @Component 注解,但是那种方式不能指定过滤规则,我们可以使用 SpringBoot 提供的 DelegatingFilterProxyRegistrationBean 来解决这个问题

@Configuration
public class FilterConfig {@Beanpublic DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() {// 构造器参数填的就是 targetBeanName,即 Filter 在 IoC 容器中的 Bean 名称DelegatingFilterProxyRegistrationBean helloFilter = new DelegatingFilterProxyRegistrationBean("helloFilter");helloFilter.addUrlPatterns("/hello");return helloFilter;}
}

分析

24 种方式类似,查看 FilterRegistrationBeanDelegatingFilterProxyRegistrationBean 的源码:

public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {...
}
public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy>implements ApplicationContextAware {...
}

可以看到两者都是实现了 AbstractFilterRegistrationBean 接口:

public abstract class AbstractFilterRegistrationBean<T extends Filter> extends DynamicRegistrationBean<Dynamic> {...
}public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean {...
}public abstract class RegistrationBean implements ServletContextInitializer, Ordered {...
}

AbstractFilterRegistrationBean 接口最终又实现了 ServletContextInitializer,对于 ServletContextInitializer 可以查看 SpringBoot - 浅析 ServletContextInitializer 如何注册 Servlet 组件 这篇文章来进一步了解。

两者的区别是:DelegatingFilterProxyRegistrationBean 通过传入的 targetBeanNameIoC 容器 中查找该 FilterBean,并通过 DelegatingFilterProxy 生成基于该 Bean 的代理 Filter 对象,而 FilterRegistrationBean 则是直接设置一个 Filter,因此该 Filter 可以被 Spring 管理也可以不用被 Spring 管理,在被 Spring 管理的情况下,可以不定义 FilterRegistrationBean,也就是第 3 种方式,这种方式无法定义拦截规则,默认过滤所有请求。

对于第 1 种方式,我们先来看一下 @ServletComponentScan 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {...
}

这个注解使用 @Import 导入了 ServletComponentScanRegistrar

对于 @Import 注解,以及 ImportBeanDefinitionRegistrar 有不明白的地方,可以先阅读 Spring - 组件(Beans)注册(到 IoC 容器)的几种方式 这篇文章

class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor";@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);if (registry.containsBeanDefinition(BEAN_NAME)) {updatePostProcessor(registry, packagesToScan);}else {addPostProcessor(registry, packagesToScan);}}...private void addPostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {GenericBeanDefinition beanDefinition = new GenericBeanDefinition();// 向 IoC 容器中注入了 ServletComponentRegisteringPostProcessorbeanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);// 并把注解中的可扫描的包路径作为入参beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(BEAN_NAME, beanDefinition);}...}

查看 ServletComponentRegisteringPostProcessor 的源码:

class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {private static final List<ServletComponentHandler> HANDLERS;static {List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>();servletComponentHandlers.add(new WebServletHandler());servletComponentHandlers.add(new WebFilterHandler());servletComponentHandlers.add(new WebListenerHandler());HANDLERS = Collections.unmodifiableList(servletComponentHandlers);}private final Set<String> packagesToScan;private ApplicationContext applicationContext;ServletComponentRegisteringPostProcessor(Set<String> packagesToScan) {this.packagesToScan = packagesToScan;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if (isRunningInEmbeddedWebServer()) {ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();for (String packageToScan : this.packagesToScan) {scanPackage(componentProvider, packageToScan);}}}private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {if (candidate instanceof AnnotatedBeanDefinition) {// 由对应的 ServletComponentHandler 进行处理,@WebServlet、@WebFilter、@WebListenerfor (ServletComponentHandler handler : HANDLERS) {handler.handle(((AnnotatedBeanDefinition) candidate),(BeanDefinitionRegistry) this.applicationContext);}}}}...
}

这里我们查看 @WebFilter 的处理类:

class WebFilterHandler extends ServletComponentHandler {WebFilterHandler() {super(WebFilter.class);}@Overridepublic void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) {// 转换为 FilterRegistrationBeanBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterRegistrationBean.class);builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));builder.addPropertyValue("filter", beanDefinition);builder.addPropertyValue("initParameters", extractInitParameters(attributes));String name = determineName(attributes, beanDefinition);builder.addPropertyValue("name", name);builder.addPropertyValue("servletNames", attributes.get("servletNames"));builder.addPropertyValue("urlPatterns", extractUrlPatterns(attributes));registry.registerBeanDefinition(name, builder.getBeanDefinition());}private EnumSet<DispatcherType> extractDispatcherTypes(Map<String, Object> attributes) {DispatcherType[] dispatcherTypes = (DispatcherType[]) attributes.get("dispatcherTypes");if (dispatcherTypes.length == 0) {return EnumSet.noneOf(DispatcherType.class);}if (dispatcherTypes.length == 1) {return EnumSet.of(dispatcherTypes[0]);}return EnumSet.of(dispatcherTypes[0], Arrays.copyOfRange(dispatcherTypes, 1, dispatcherTypes.length));}private String determineName(Map<String, Object> attributes, BeanDefinition beanDefinition) {return (String) (StringUtils.hasText((String) attributes.get("filterName")) ? attributes.get("filterName"): beanDefinition.getBeanClassName());}}

1 种方式最终也是采用第 2 种方式

SpringBoot - 配置 Filter 的几种方式相关推荐

  1. SpringBoot配置绑定的两种方式

    SpringBoot配置绑定的两种方式 演示文件 bean public class Student {private String name;private Integer age;public S ...

  2. springboot配置Filter的两种方法

    一.使用注解 1. 假设Filter类的路径为com.sanro.filter @Slf4j @WebFilter(filterName = "authFilter", urlPa ...

  3. SpringBoot自定义Filter的三种方式

    方式一 : 使用OncePerRequestFilter OncePerRequestFilter该过滤器,是spring提供的.默认情况下,每一个请求都会经过该过滤器 方式二: @WebFilter ...

  4. java手动注册filter,SpringBoot注册Filter的两种实现方式

    springboot版本:2.2.5 一.filter注册 springboot中添加filter有两种方式: 1.实现方法一 2.实现方法二 二.Springboot自带filter Springb ...

  5. SpringBoot定时任务实现的两种方式介绍

    今天给大家介绍SpringBoot定时任务实现的几种方式,希望对大家能有所帮助! 1.SpringTask 用法 框架介绍:SpringTask是Spring自带的轻量级定时任务工具,相比于Quart ...

  6. SpringBoot 配置文件加密的两种方式

    SpringBoot配置文件加密的两种方式 jasypt使用方式 用法一: 1.Application.java上增加注解@EnableEncryptableProperties(jasypt-spr ...

  7. @Import注解:导入配置类的四种方式源码解析

    微信搜索:码农StayUp 主页地址:https://gozhuyinglong.github.io 源码分享:https://github.com/gozhuyinglong/blog-demos ...

  8. springboot读取配置文件的三种方式

    项目中springboot读取配置文件的三种方式: 1.使用Environment 2.使用@Value 3.使用@ConfigurationProperties注解映射到bean中,定义一个User ...

  9. 【转】Apache 配置虚拟主机三种方式

    Apache 配置虚拟主机三种方式 原文博客http://www.cnblogs.com/hi-bazinga/archive/2012/04/23/2466605.html 一.基于IP 1. 假设 ...

最新文章

  1. 总结FormsAuthentication的使用
  2. 浅析网站域名申请注册的四种常见方式
  3. 洛谷——P2678 跳石头
  4. Oracle12cR2的CDB与PDB简单管理操作
  5. asp.net中的窗体身份验证(分目录验证篇)
  6. ei eo eq什么意思_EI源刊是什么意思
  7. 【转】4.2使用jQuery.form插件,实现完美的表单异步提交
  8. android 软键盘 状态,Android监听软键盘状态
  9. gradle配置到阿里云_通过图文步骤的方式,带你配置阿里云服务器搭建网站
  10. Nginx配置Thinkphp3.2.3配置,访问Nginx报 No input file specified. 的问题解决
  11. django 用auth的login后重定向
  12. 【ACM夏训】综合训练赛
  13. 延时芯片C005介绍
  14. smart原则_OKR 文化:用 SMART 原则量化目标
  15. 实例详解ISA防火墙策略元素:ISA2006系列之五
  16. 计算基因上外显子碱基覆盖度(exon coverage depth):Samtool工具使用
  17. android手机更改手机密码,手机忘记密码如可解决 安卓手机重置密码教程【详解】...
  18. 利用archetype创建maven脚手架和新项目
  19. 比我聪明漂亮还比我努力的人,告诉我10个tips
  20. 数据可视化大屏应用场景有哪些?

热门文章

  1. i910980hk和r9 4900h哪个好
  2. 08. Volume Framework||Volume Profile
  3. Java虚拟机(三)--------GC算法和收集器
  4. 【Linux】Supervisor使用详解
  5. Windows 安装ffmpeg并从视频中提取音频
  6. 商标销售可以是有利可图的业务
  7. ExtJs自学教程(一)
  8. 各国时区夏令时切换信息
  9. 第四章 JavaWeb CSS入门 核心基础 基础形式 + 选择器
  10. 1231231312