• SpringBoot - 配置 Filter 的几种方式


    前言

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

    配置

    1、使用原生注解

    • 首先定义一个 Filter 类,匹配 /hello 请求:
    @WebFilter(filterName = "myFilter", urlPatterns = "/hello")
    public class MyFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            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() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 使用 @ServletComponentScan 注解扫描原生组件
    @SpringBootApplication
    @ServletComponentScan(basePackages = "com.example.springboot.review.filter")
    public class SpringBootReviewApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootReviewApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、使用 SpringBoot 提供的 FilterRegistrationBean

    • 定义一个 LoggerFilter
    public class LoggerFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("init LoggerFilter");
        }
    
        @Override
        public 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");
        }
    
        @Override
        public void destroy() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 在配置类中使用 FilterRegistrationBean 注册 Filter
    @Configuration
    public class FilterConfig {
    
        @Bean
        public 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

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

    @Component
    @Order(-1)	// 可以指定优先级,不填的话默认为最小的优先级
    public class HelloFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("init HelloFilter");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("HelloFilter doFilter");
            chain.doFilter(request, response);
        }
    
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    或者:

    @Configuration
    public class FilterConfig {
        
        @Bean
        public Filter filter() {
            return (request, response, chain) -> {
                System.out.println("innerFilter doFilter");
                chain.doFilter(request, response);
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

    @Configuration
    public class FilterConfig {
    
        @Bean
        public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() {
        	// 构造器参数填的就是 targetBeanName,即 Filter 在 IoC 容器中的 Bean 名称
            DelegatingFilterProxyRegistrationBean helloFilter = new DelegatingFilterProxyRegistrationBean("helloFilter");
            helloFilter.addUrlPatterns("/hello");
            return helloFilter;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    分析

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

    public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
    	...
    }
    
    • 1
    • 2
    • 3
    public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy>
    		implements ApplicationContextAware {
    	...
    }
    
    • 1
    • 2
    • 3
    • 4

    可以看到两者都是实现了 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 {
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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 {
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

    class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
    
    	private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor";
    
    	@Override
    	public 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 容器中注入了 ServletComponentRegisteringPostProcessor
    		beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
    		// 并把注解中的可扫描的包路径作为入参
    		beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);
    		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    		registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
    	}
    
    	...
    
    }
    
    • 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

    查看 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;
    	}
    
    	@Override
    	public 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、@WebListener
    				for (ServletComponentHandler handler : HANDLERS) {
    					handler.handle(((AnnotatedBeanDefinition) candidate),
    							(BeanDefinitionRegistry) this.applicationContext);
    				}
    			}
    		}
    	}
    
    	...
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

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

    class WebFilterHandler extends ServletComponentHandler {
    
    	WebFilterHandler() {
    		super(WebFilter.class);
    	}
    
    	@Override
    	public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,
    			BeanDefinitionRegistry registry) {
    		// 转换为 FilterRegistrationBean
    		BeanDefinitionBuilder 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
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39

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

  • 相关阅读:
    SQL引擎子系统的工作原理
    扑克牌大小(Java语言)
    英语语法 - 宾语从句
    12基于MATLAB的短时傅里叶变换( STFT),连续小波变换( CWT),程序已调通,可以直接运行。
    英特尔神经网络计算棒
    点评项目核心内容
    【LeetCode-中等题】39. 组合总和
    漂浮式风机 | 理论分析 | 势流理论与水动力
    NPM使用
    为什么分类问题不能使用mse损失函数,更容易理解版本
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/125658982