0


SpringBoot - 配置 Filter 的几种方式

前言

在 SpringMVC - 对于如何配置 Filter 的深度剖析 这篇文章中,我们知道了在

SpringMVC

环境中如何配置

Filter

,接下来我们看一下如何在

SpringBoot

中配置

Filter

配置

1、使用原生注解

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

2、使用

SpringBoot

提供的

FilterRegistrationBean
  • 定义一个 LoggerFilter
publicclassLoggerFilterimplementsFilter{@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{System.out.println("init LoggerFilter");}@OverridepublicvoiddoFilter(ServletRequest request,ServletResponse response,FilterChain chain)throwsIOException,ServletException{System.out.println("LoggerFilter before doFilter");
        chain.doFilter(request, response);System.out.println("LoggerFilter after doFilter");}@Overridepublicvoiddestroy(){}}
  • 在配置类中使用 FilterRegistrationBean 注册 Filter
@ConfigurationpublicclassFilterConfig{@BeanpublicFilterRegistrationBean<LoggerFilter>filterRegistrationBean(){FilterRegistrationBean<LoggerFilter> bean =newFilterRegistrationBean<>();
        bean.setFilter(newLoggerFilter());// 这里可以使用 new,也可以在 Filter 上加 @Component 注入进来
        bean.addUrlPatterns("/hello");
        bean.setName("loggerFilter");
        bean.setOrder(1);// 值越小,优先级越高return bean;}// 可以写多个 FilterRegistrationBean}

3、直接在

Filter

上使用

@Component

注解

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

@Component@Order(-1)// 可以指定优先级,不填的话默认为最小的优先级publicclassHelloFilterimplementsFilter{@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{System.out.println("init HelloFilter");}@OverridepublicvoiddoFilter(ServletRequest request,ServletResponse response,FilterChain chain)throwsIOException,ServletException{System.out.println("HelloFilter doFilter");
        chain.doFilter(request, response);}@Overridepublicvoiddestroy(){Filter.super.destroy();}}

或者:

@ConfigurationpublicclassFilterConfig{@BeanpublicFilterfilter(){return(request, response, chain)->{System.out.println("innerFilter doFilter");
            chain.doFilter(request, response);};}}

4、使用

DelegatingFilterProxyRegistrationBean

注册已经注册为

Bean

Filter

在上面 3 中,我们给

Filter

类上加了

@Component

注解,但是那种方式不能指定过滤规则,我们可以使用

SpringBoot

提供的

DelegatingFilterProxyRegistrationBean

来解决这个问题

@ConfigurationpublicclassFilterConfig{@BeanpublicDelegatingFilterProxyRegistrationBeandelegatingFilterProxyRegistrationBean(){// 构造器参数填的就是 targetBeanName,即 Filter 在 IoC 容器中的 Bean 名称DelegatingFilterProxyRegistrationBean helloFilter =newDelegatingFilterProxyRegistrationBean("helloFilter");
        helloFilter.addUrlPatterns("/hello");return helloFilter;}}

分析

2

4

种方式类似,查看

FilterRegistrationBean

DelegatingFilterProxyRegistrationBean

的源码:

publicclassFilterRegistrationBean<TextendsFilter>extendsAbstractFilterRegistrationBean<T>{...}
publicclassDelegatingFilterProxyRegistrationBeanextendsAbstractFilterRegistrationBean<DelegatingFilterProxy>implementsApplicationContextAware{...}

可以看到两者都是实现了

AbstractFilterRegistrationBean

接口:

publicabstractclassAbstractFilterRegistrationBean<TextendsFilter>extendsDynamicRegistrationBean<Dynamic>{...}publicabstractclassDynamicRegistrationBean<DextendsRegistration.Dynamic>extendsRegistrationBean{...}publicabstractclassRegistrationBeanimplementsServletContextInitializer,Ordered{...}

AbstractFilterRegistrationBean

接口最终又实现了

ServletContextInitializer

,对于

ServletContextInitializer

可以查看 SpringBoot - 浅析 ServletContextInitializer 如何注册 Servlet 组件 这篇文章来进一步了解。

两者的区别是:

DelegatingFilterProxyRegistrationBean

通过传入的

targetBeanName

IoC 容器

中查找该

Filter

Bean

,并通过

DelegatingFilterProxy

生成基于该

Bean

的代理

Filter

对象,而

FilterRegistrationBean

则是直接设置一个

Filter

,因此该

Filter

可以被

Spring

管理也可以不用被

Spring

管理,在被

Spring

管理的情况下,可以不定义

FilterRegistrationBean

,也就是第

3

种方式,这种方式无法定义拦截规则,默认过滤所有请求。

对于第

1

种方式,我们先来看一下

@ServletComponentScan

注解:

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

这个注解使用

@Import

导入了

ServletComponentScanRegistrar

对于

@Import

注解,以及

ImportBeanDefinitionRegistrar

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

classServletComponentScanRegistrarimplementsImportBeanDefinitionRegistrar{privatestaticfinalString BEAN_NAME ="servletComponentRegisteringPostProcessor";@OverridepublicvoidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry){Set<String> packagesToScan =getPackagesToScan(importingClassMetadata);if(registry.containsBeanDefinition(BEAN_NAME)){updatePostProcessor(registry, packagesToScan);}else{addPostProcessor(registry, packagesToScan);}}...privatevoidaddPostProcessor(BeanDefinitionRegistry registry,Set<String> packagesToScan){GenericBeanDefinition beanDefinition =newGenericBeanDefinition();// 向 IoC 容器中注入了 ServletComponentRegisteringPostProcessor
        beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);// 并把注解中的可扫描的包路径作为入参
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN_NAME, beanDefinition);}...}

查看

ServletComponentRegisteringPostProcessor

的源码:

classServletComponentRegisteringPostProcessorimplementsBeanFactoryPostProcessor,ApplicationContextAware{privatestaticfinalList<ServletComponentHandler> HANDLERS;static{List<ServletComponentHandler> servletComponentHandlers =newArrayList<>();
        servletComponentHandlers.add(newWebServletHandler());
        servletComponentHandlers.add(newWebFilterHandler());
        servletComponentHandlers.add(newWebListenerHandler());
        HANDLERS =Collections.unmodifiableList(servletComponentHandlers);}privatefinalSet<String> packagesToScan;privateApplicationContext applicationContext;ServletComponentRegisteringPostProcessor(Set<String> packagesToScan){this.packagesToScan = packagesToScan;}@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throwsBeansException{if(isRunningInEmbeddedWebServer()){ClassPathScanningCandidateComponentProvider componentProvider =createComponentProvider();for(String packageToScan :this.packagesToScan){scanPackage(componentProvider, packageToScan);}}}privatevoidscanPackage(ClassPathScanningCandidateComponentProvider componentProvider,String packageToScan){for(BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)){if(candidate instanceofAnnotatedBeanDefinition){// 由对应的 ServletComponentHandler 进行处理,@WebServlet、@WebFilter、@WebListenerfor(ServletComponentHandler handler : HANDLERS){
                    handler.handle(((AnnotatedBeanDefinition) candidate),(BeanDefinitionRegistry)this.applicationContext);}}}}...}

这里我们查看

@WebFilter

的处理类:

classWebFilterHandlerextendsServletComponentHandler{WebFilterHandler(){super(WebFilter.class);}@OverridepublicvoiddoHandle(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());}privateEnumSet<DispatcherType>extractDispatcherTypes(Map<String,Object> attributes){DispatcherType[] dispatcherTypes =(DispatcherType[]) attributes.get("dispatcherTypes");if(dispatcherTypes.length ==0){returnEnumSet.noneOf(DispatcherType.class);}if(dispatcherTypes.length ==1){returnEnumSet.of(dispatcherTypes[0]);}returnEnumSet.of(dispatcherTypes[0],Arrays.copyOfRange(dispatcherTypes,1, dispatcherTypes.length));}privateStringdetermineName(Map<String,Object> attributes,BeanDefinition beanDefinition){return(String)(StringUtils.hasText((String) attributes.get("filterName"))? attributes.get("filterName"): beanDefinition.getBeanClassName());}}

1

种方式最终也是采用第

2

种方式

标签: spring boot java spring

本文转载自: https://blog.csdn.net/qiaohao0206/article/details/125658982
版权归原作者 云原生.乔豆麻袋.cn 所有, 如有侵权,请联系我们删除。

“SpringBoot - 配置 Filter 的几种方式”的评论:

还没有评论