营销型网站四大功能/百度品牌广告收费标准
文章目录
- 1. 前言
- 2. @OrderedWebFilter 注解实现
- 2.1 @OrderedWebFilter 注解定义
- 2.2 @OrderedWebFilter 注解后置处理器实现
1. 前言
在 SpringBoot 中,开发者可以使用 @WebFilter
注解来实现过滤器 Filter 的定义,但是这个注解功能并不完善,其在日常使用中至少有以下几个缺陷,有鉴于此笔者打算自行定义实现一个功能更为完善的@OrderedWebFilter
注解
@WebFilter
注解想要正常生效必须在启动类上添加@ServletComponentScan
注解,手动开启扫描注册@WebFilter
注解不能指定过滤器 Filter 的拦截顺序
2. @OrderedWebFilter 注解实现
2.1 @OrderedWebFilter 注解定义
@OrderedWebFilter
注解的定义如下所示,其使用方式与 @WebFilter
注解完全一致,但是可以通过 order 属性 指定拦截器的执行顺序
/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements. See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License. You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package cn.huolala.callcenter.filter.annotation;import org.springframework.core.Ordered;import javax.servlet.DispatcherType;
import javax.servlet.annotation.WebInitParam;
import java.lang.annotation.*;/*** The annotation used to declare a Servlet {@link javax.servlet.Filter}. <br>* <br>** This annotation will be processed by the container during deployment, the* Filter class in which it is found will be created as per the configuration* and applied to the URL patterns, {@link javax.servlet.Servlet}s and* {@link DispatcherType}s.<br>* <br>** If the name attribute is not defined, the fully qualified name of the class* is used.<br>* <br>** At least one URL pattern MUST be declared in either the {@code value} or* {@code urlPattern} attribute of the annotation, but not both.<br>* <br>** The {@code value} attribute is recommended for use when the URL pattern is* the only attribute being set, otherwise the {@code urlPattern} attribute* should be used.<br>* <br>** The annotated class MUST implement {@link javax.servlet.Filter}.** E.g.** <code>@OrderedWebFilter("/path/*")</code><br>* <code>public class AnExampleFilter implements Filter { ... </code><br>** @since Servlet 3.0 (Section 8.1.2)**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OrderedWebFilter {/*** @return description of the Filter, if present*/String description() default "";/*** @return display name of the Filter, if present*/String displayName() default "";/*** @return array of initialization params for this Filter*/WebInitParam[] initParams() default {};/*** @return name of the Filter, if present*/String filterName() default "";/*** @return small icon for this Filter, if present*/String smallIcon() default "";/*** @return the large icon for this Filter, if present*/String largeIcon() default "";/*** @return array of Servlet names to which this Filter applies*/String[] servletNames() default {};/*** A convenience method, to allow extremely simple annotation of a class.** @return array of URL patterns* @see #urlPatterns()*/String[] value() default {};/*** @return array of URL patterns to which this Filter applies*/String[] urlPatterns() default {};/*** @return array of DispatcherTypes to which this filter applies*/DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};/*** @return asynchronous operation supported by this Filter*/boolean asyncSupported() default false;int order() default Ordered.LOWEST_PRECEDENCE;
}
2.2 @OrderedWebFilter 注解后置处理器实现
我们知道 SpringBoot 中的一切增强功能都是通过后置处理器实现的,@OrderedWebFilter
注解的后置处理器 OrderedWebFilterProcessor
实现如下,需要注意的有以下几点:
OrderedWebFilterProcessor
后置处理器通过 SpringBoot 的条件注解@ConditionalOnWebApplication
和@ConditionalOnClass
指定了该注解发挥作用的前提是应用必须为 SERVLET 类型,且必须要有特定类存在OrderedWebFilterProcessor
后置处理器实现了 SpringBoot 的扩展接口BeanFactoryPostProcessor
和ApplicationContextAware
,这也是该处理器能够运行增强特定类的关键入口OrderedWebFilterProcessor
后置处理器的关键逻辑是在后置增强BenFactory
中通过组件扫描器ClassPathScanningCandidateComponentProvider
扫描指定路径下的被标注了@OrderedWebFilter
注解的类,扫描到之后就将原来的BeanDefintion
包装到FilterRegistrationBean
,并将@OrderedWebFilter
注解的属性填充到FilterRegistrationBean
中,从而自动实现了过滤器 Filter 的配置
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.DispatcherType;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({WebMvcConfigurer.class, HandlerInterceptor.class})
public class OrderedWebFilterProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {private ApplicationContext applicationContext;private boolean isRunningInEmbeddedWebServer() {return this.applicationContext instanceof WebApplicationContext&& ((WebApplicationContext) this.applicationContext).getServletContext() == null;}private ClassPathScanningCandidateComponentProvider createComponentProvider() {ClassPathScanningCandidateComponentProvider componentProvider =new ClassPathScanningCandidateComponentProvider(false);componentProvider.setEnvironment(this.applicationContext.getEnvironment());componentProvider.setResourceLoader(this.applicationContext);componentProvider.addIncludeFilter(new AnnotationTypeFilter(OrderedWebFilter.class));return componentProvider;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if (!isRunningInEmbeddedWebServer()) {return;}ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();// 设置扫描 basePackageString basePackage = this.applicationContext.getEnvironment().getProperty("web.filter.basePackage", "cn.nathan.*");for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {if (!(candidate instanceof AnnotatedBeanDefinition)) {continue;}Map<String, Object> attributes = ((AnnotatedBeanDefinition) candidate).getMetadata().getAnnotationAttributes(OrderedWebFilter.class.getName());if (attributes != null) {doHandle(attributes, ((AnnotatedBeanDefinition) candidate), (BeanDefinitionRegistry) this.applicationContext);}}}public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterRegistrationBean.class);builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));// 设置优先级builder.addPropertyValue("order", attributes.get("order"));builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));builder.addPropertyValue("filter", beanDefinition);builder.addPropertyValue("initParameters", extractInitParameters(attributes));builder.addPropertyValue("servletNames", attributes.get("servletNames"));builder.addPropertyValue("urlPatterns", extractUrlPatterns(attributes));String name = determineName(attributes, beanDefinition);builder.addPropertyValue("name", name);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());}protected String[] extractUrlPatterns(Map<String, Object> attributes) {String[] value = (String[]) attributes.get("value");String[] urlPatterns = (String[]) attributes.get("urlPatterns");if (urlPatterns.length > 0) {Assert.state(value.length == 0, "The urlPatterns and value attributes are mutually exclusive.");return urlPatterns;}return value;}protected final Map<String, String> extractInitParameters(Map<String, Object> attributes) {Map<String, String> initParameters = new HashMap<>();for (AnnotationAttributes initParam : (AnnotationAttributes[]) attributes.get("initParams")) {String name = (String) initParam.get("name");String value = (String) initParam.get("value");initParameters.put(name, value);}return initParameters;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}}