SpringBoot - 配置 Filter 的几种方式
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() {
}
}
- 使用
@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 {
@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() {
}
}
- 在配置类中使用
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
}
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();
}
}
或者:
@Configuration
public class FilterConfig {
@Bean
public Filter filter() {
return (request, response, chain) -> {
System.out.println("innerFilter doFilter");
chain.doFilter(request, response);
};
}
}
4、使用 DelegatingFilterProxyRegistrationBean
注册已经注册为 Bean
的 Filter
在上面 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;
}
}
分析
第 2
、4
种方式类似,查看 FilterRegistrationBean
和 DelegatingFilterProxyRegistrationBean
的源码:
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
通过传入的 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 @interface ServletComponentScan {
...
}
这个注解使用 @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);
}
...
}
查看 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);
}
}
}
}
...
}
这里我们查看 @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
种方式
更多推荐
所有评论(0)