赞
踩
SpringBoot中是可以自行注册Filter的,在SpringBoot中有几种注册Filter的方式。
- @Configuration
- public class FilterConfig {
- @Bean
- public FilterRegistrationBean filterRegistrationBean(){
- FilterRegistrationBean bean = new FilterRegistrationBean();
- bean.setFilter(new MyFilter2());
- bean.addUrlPatterns("/*");
- return bean;
- }
-
- @Bean
- public FilterRegistrationBean filterRegistrationBean(){
- FilterRegistrationBean bean = new FilterRegistrationBean();
- bean.setFilter(new MyFilter1());
- bean.addUrlPatterns("/*");
- return bean;
- }
- }
-
- public class MyFilter1 implements Filter {
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- }
-
- @Override
- public void destroy() {
- }
- }
-
- public class MyFilter2 implements Filter {
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- }
-
- @Override
- public void destroy() {
- }
- }
-
- @Order(1)
- @WebFilter(urlPatterns = "*.json", filterName = "reqResFilter")
- public class ReqResFilter implements Filter {
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
- throws IOException, ServletException {
- }
-
- @Override
- public void destroy() {
- }
- }
-
- @SpringBootApplication
- @ServletComponentScan(basePackages = "com.dm.base.filter")
- public class MyApplication {
- public static void main(String[] args) {
- SpringApplication.run(MyApplication.class, args);
- }
- }
关于这两个注解有如下说明:
When using an embedded container, automatic registration of @WebServlet, @WebFilter, and @WebListener annotated classes can be enabled using @ServletComponentScan.
使用嵌入式容器时,可以使用@ServletComponentScan启用@WebServlet,@ WebFilter和@WebListener注释类的自动注册。
@ServletComponentScan will have no effect in a standalone container, where the container’s built-in discovery mechanisms will be used instead.
如果使用外置容器的话,就不需要使用这条注解,因为外置容器的内置发现机制将会替代这个注解的效果。
上面有使用@Order注解指定一个int值,越小越先执行。很多博客文章都是这么说的,但你真正的试了吗?真的可以使用这个注解指定顺序吗?答案是否定的。经过测试,发现 @Order 注解指定 int 值没有起作用,是无效的。为啥?因为看源码发现 @WebFilter 修饰的过滤器在加载时,没有使用 @Order 注解,而是使用的类名来实现自定义Filter顺序,所以这种方式下想定义Filter的顺序,就必须限定 Filter 的类名,比如刚才那个 Filter 叫 ReqResFilter ,加入我们现在新写了一个 Filter 叫 AlibabaFilter ,那么顺序就是 AlibabaFilter > ReqResFilter 。所以这种方式虽然实现起来简单,只需要注解,但自定义顺序就必须要限定类名,使用类名达到排序效果了。
上面说的都是SpringBoot对Filter提供的使用方式,但是对于Servlet容器来讲,处理方式应该是统一的,毕竟SpringBoot最终还是会调用Servlet容器的接口来实现。首先来说,Filter处理逻辑是在DispatcherServlet之前的,也就是说,如果到了DispatcherServlet了,说明所有Filter逻辑已经处理完毕了。
spring boot默认的几个filter,按先后顺序分别是,可以看到他们的顺序都是依赖一个int类型的order变量决定的,数值越小,优先级越高。
- // 编码过滤器
- public class OrderedCharacterEncodingFilter extends CharacterEncodingFilter implements OrderedFilter {
- // -2147483648
- private int order = Ordered.HIGHEST_PRECEDENCE;
- ...
- }
-
- // Form内容处理过滤器
- public class OrderedFormContentFilter extends FormContentFilter implements OrderedFilter {
- // -9900
- private int order = DEFAULT_ORDER;
-
- }
-
- // 请求的上下文环境处理了过滤器
- public class OrderedRequestContextFilter extends RequestContextFilter implements OrderedFilter {
- // -105
- private int order = REQUEST_WRAPPER_FILTER_MAX_ORDER - 105;
-
- }
-
那我们自定义的filter顺序由谁决定?可以探索一下,首先我们知道,顺序是按照filterChain变量里的filter依次执行的,所以我们需要知道filterChain是如何创建的?每次请求都会重新生成一个filterChain。看看如下方法:
org.apache.catalina.core.ApplicationFilterFactory#createFilterChain(...)
- public static ApplicationFilterChain createFilterChain(ServletRequest request,
- Wrapper wrapper, Servlet servlet) {
-
- // If there is no servlet to execute, return null
- if (servlet == null)
- return null;
-
- // Create and initialize a filter chain object
- ApplicationFilterChain filterChain = null;
- if (request instanceof Request) {
- Request req = (Request) request;
- if (Globals.IS_SECURITY_ENABLED) {
- // Security: Do not recycle
- filterChain = new ApplicationFilterChain();
- } else {
- filterChain = (ApplicationFilterChain) req.getFilterChain();
- if (filterChain == null) {
- filterChain = new ApplicationFilterChain();
- req.setFilterChain(filterChain);
- }
- }
- } else {
- // Request dispatcher in use
- filterChain = new ApplicationFilterChain();
- }
-
- filterChain.setServlet(servlet);
- filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
-
- // Acquire the filter mappings for this Context
- StandardContext context = (StandardContext) wrapper.getParent();
- FilterMap filterMaps[] = context.findFilterMaps();
-
- // If there are no filter mappings, we are done
- if ((filterMaps == null) || (filterMaps.length == 0))
- return filterChain;
-
- // Acquire the information we will need to match filter mappings
- DispatcherType dispatcher =
- (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
-
- String requestPath = null;
- Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
- if (attribute != null){
- requestPath = attribute.toString();
- }
-
- String servletName = wrapper.getName();
-
- // Add the relevant path-mapped filters to this filter chain
- for (FilterMap filterMap : filterMaps) {
- if (!matchDispatcher(filterMap, dispatcher)) {
- continue;
- }
- if (!matchFiltersURL(filterMap, requestPath))
- continue;
- ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
- context.findFilterConfig(filterMap.getFilterName());
- if (filterConfig == null) {
- // FIXME - log configuration problem
- continue;
- }
- filterChain.addFilter(filterConfig);
- }
-
- // Add filters that match on servlet name second
- for (FilterMap filterMap : filterMaps) {
- if (!matchDispatcher(filterMap, dispatcher)) {
- continue;
- }
- if (!matchFiltersServlet(filterMap, servletName))
- continue;
- ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
- context.findFilterConfig(filterMap.getFilterName());
- if (filterConfig == null) {
- // FIXME - log configuration problem
- continue;
- }
- filterChain.addFilter(filterConfig);
- }
-
- // Return the completed filter chain
- return filterChain;
- }
我们需要关注的关键点是这句代码(32行):FilterMap filterMaps[] = context.findFilterMaps();这个数组决定了filter的顺序,这里的context是org.apache.catalina.core.StandardContext,相关的方法有2个,但是这里有关的是addFilterMapBefore(...)。一个FilterMap对应一个Filter,这个方法会被调用多次的。
StandardContext#addFilterMap(),StandardContext#addFilterMapBefore()
- @Override
- public void addFilterMapBefore(FilterMap filterMap) {
- validateFilterMap(filterMap);
- // Add this filter mapping to our registered set
- filterMaps.addBefore(filterMap);
- fireContainerEvent("addFilterMap", filterMap);
- }
接下来我们需要看看谁在调用addFilterMapBefore(...),是下面方法
org.apache.catalina.core.ApplicationFilterRegistration#addMappingForUrlPatterns(...)。这个方法生成FilterMap并添加,所以关键点是谁调用这个方法以及其调用顺序。
- @Override
- public void addMappingForUrlPatterns(
- EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
- String... urlPatterns) {
-
- FilterMap filterMap = new FilterMap();
-
- filterMap.setFilterName(filterDef.getFilterName());
-
- if (dispatcherTypes != null) {
- for (DispatcherType dispatcherType : dispatcherTypes) {
- filterMap.setDispatcher(dispatcherType.name());
- }
- }
-
- if (urlPatterns != null) {
- // % decoded (if necessary) using UTF-8
- for (String urlPattern : urlPatterns) {
- filterMap.addURLPattern(urlPattern);
- }
-
- if (isMatchAfter) {
- context.addFilterMap(filterMap);
- } else {
- context.addFilterMapBefore(filterMap);
- }
- }
- // else error?
-
- }
找到调用地方如下org.springframework.boot.web.servlet.AbstractFilterRegistrationBean#configure(...),这里就到了springboot控制了,他是衔接springboot和servlet的地方,需要注意下这个衔接点。
- @Override
- protected void configure(FilterRegistration.Dynamic registration) {
- super.configure(registration);
- EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
- if (dispatcherTypes == null) {
- dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
- }
- Set<String> servletNames = new LinkedHashSet<>();
- for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
- servletNames.add(servletRegistrationBean.getServletName());
- }
- servletNames.addAll(this.servletNames);
- if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
- registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
- }
- else {
- // 这里调用
- if (!servletNames.isEmpty()) {
- registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
- StringUtils.toStringArray(servletNames));
- }
- if (!this.urlPatterns.isEmpty()) {
- registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
- StringUtils.toStringArray(this.urlPatterns));
- }
- }
- }
也就是说这个configure(..)方法也会被调用多次,继续追查看看谁在调用他。
org.springframework.boot.web.servlet.DynamicRegistrationBean#register(...)
- @Override
- protected final void register(String description, ServletContext servletContext) {
- D registration = addRegistration(description, servletContext);
- if (registration == null) {
- logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
- return;
- }
- // 这里调用
- configure(registration);
- }
接着看调用链org.springframework.boot.web.servlet.RegistrationBean#onStartup(...)
- @Override
- public final void onStartup(ServletContext servletContext) throws ServletException {
- String description = getDescription();
- if (!isEnabled()) {
- logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
- return;
- }
- register(description, servletContext);
- }
接着看调用链org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize(...),这个方法只调用一次,下面的filter通过遍历生成。
- private void selfInitialize(ServletContext servletContext) throws ServletException {
- prepareWebApplicationContext(servletContext);
- registerApplicationScope(servletContext);
- WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
- for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
- // 这里遍历调用
- beans.onStartup(servletContext);
- }
- }
也就是说关键点在org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getServletContextInitializerBeans()方法上
- protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
- return new ServletContextInitializerBeans(getBeanFactory());
- }
继续追调用org.springframework.boot.web.servlet.ServletContextInitializerBeans构造函数
- @SafeVarargs
- public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
- Class<? extends ServletContextInitializer>... initializerTypes) {
- this.initializers = new LinkedMultiValueMap<>();
- this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
- : Collections.singletonList(ServletContextInitializer.class);
- addServletContextInitializerBeans(beanFactory);
- addAdaptableBeans(beanFactory);
- // 排序就在这里
- List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
- .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
- .collect(Collectors.toList());
- this.sortedList = Collections.unmodifiableList(sortedInitializers);
- logMappings(this.initializers);
- }
所以现在知道这个排序是怎么决定了吧,也即@Order注解,注意,如果这个Bean不是受Spring管理的就不会生效的。不生效的话默认值是2147483647,这样顺序会在最后面。
现在我们顺着来捋一下这个关系,其中有一个关键点需要明确的是,通常情况下,我们springboot应用是servlet容器的,所以实例化的ApplicationContext类型是:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext。有了这个确定的类型,很多链路就变得清晰了。可以看到最后是丢给线程池来处理了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。