当前位置:   article > 正文

SpringBoot之Filter注册_springboot 注册filter

springboot 注册filter

SpringBoot中是可以自行注册Filter的,在SpringBoot中有几种注册Filter的方式。

一、通过org.springframework.boot.web.servlet.FilterRegistrationBean注册

  1. @Configuration
  2. public class FilterConfig {
  3. @Bean
  4. public FilterRegistrationBean filterRegistrationBean(){
  5. FilterRegistrationBean bean = new FilterRegistrationBean();
  6. bean.setFilter(new MyFilter2());
  7. bean.addUrlPatterns("/*");
  8. return bean;
  9. }
  10. @Bean
  11. public FilterRegistrationBean filterRegistrationBean(){
  12. FilterRegistrationBean bean = new FilterRegistrationBean();
  13. bean.setFilter(new MyFilter1());
  14. bean.addUrlPatterns("/*");
  15. return bean;
  16. }
  17. }
  18. public class MyFilter1 implements Filter {
  19. @Override
  20. public void init(FilterConfig filterConfig) throws ServletException {
  21. }
  22. @Override
  23. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  24. }
  25. @Override
  26. public void destroy() {
  27. }
  28. }
  29. public class MyFilter2 implements Filter {
  30. @Override
  31. public void init(FilterConfig filterConfig) throws ServletException {
  32. }
  33. @Override
  34. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  35. }
  36. @Override
  37. public void destroy() {
  38. }
  39. }

二、通过 @WebFilter + @ServletComponentScan 注解实现

  1. @Order(1)
  2. @WebFilter(urlPatterns = "*.json", filterName = "reqResFilter")
  3. public class ReqResFilter implements Filter {
  4. @Override
  5. public void init(FilterConfig filterConfig) throws ServletException {
  6. }
  7. @Override
  8. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
  9.                  throws IOException, ServletException {
  10. }
  11. @Override
  12. public void destroy() {
  13. }
  14. }
  15. @SpringBootApplication
  16. @ServletComponentScan(basePackages = "com.dm.base.filter")
  17. public class MyApplication {
  18. public static void main(String[] args) {
  19. SpringApplication.run(MyApplication.class, args);
  20. }
  21. }

关于这两个注解有如下说明:

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 。所以这种方式虽然实现起来简单,只需要注解,但自定义顺序就必须要限定类名,使用类名达到排序效果了。

三、Servlet对Filter处理

上面说的都是SpringBoot对Filter提供的使用方式,但是对于Servlet容器来讲,处理方式应该是统一的,毕竟SpringBoot最终还是会调用Servlet容器的接口来实现。首先来说,Filter处理逻辑是在DispatcherServlet之前的,也就是说,如果到了DispatcherServlet了,说明所有Filter逻辑已经处理完毕了。

    spring boot默认的几个filter,按先后顺序分别是,可以看到他们的顺序都是依赖一个int类型的order变量决定的,数值越小,优先级越高。

  1. // 编码过滤器
  2. public class OrderedCharacterEncodingFilter extends CharacterEncodingFilter implements OrderedFilter {
  3. // -2147483648
  4. private int order = Ordered.HIGHEST_PRECEDENCE;
  5. ...
  6. }
  7. // Form内容处理过滤器
  8. public class OrderedFormContentFilter extends FormContentFilter implements OrderedFilter {
  9. // -9900
  10. private int order = DEFAULT_ORDER;
  11. }
  12. // 请求的上下文环境处理了过滤器
  13. public class OrderedRequestContextFilter extends RequestContextFilter implements OrderedFilter {
  14. // -105
  15. private int order = REQUEST_WRAPPER_FILTER_MAX_ORDER - 105;
  16. }

那我们自定义的filter顺序由谁决定?可以探索一下,首先我们知道,顺序是按照filterChain变量里的filter依次执行的,所以我们需要知道filterChain是如何创建的?每次请求都会重新生成一个filterChain。看看如下方法:

org.apache.catalina.core.ApplicationFilterFactory#createFilterChain(...)

  1. public static ApplicationFilterChain createFilterChain(ServletRequest request,
  2. Wrapper wrapper, Servlet servlet) {
  3. // If there is no servlet to execute, return null
  4. if (servlet == null)
  5. return null;
  6. // Create and initialize a filter chain object
  7. ApplicationFilterChain filterChain = null;
  8. if (request instanceof Request) {
  9. Request req = (Request) request;
  10. if (Globals.IS_SECURITY_ENABLED) {
  11. // Security: Do not recycle
  12. filterChain = new ApplicationFilterChain();
  13. } else {
  14. filterChain = (ApplicationFilterChain) req.getFilterChain();
  15. if (filterChain == null) {
  16. filterChain = new ApplicationFilterChain();
  17. req.setFilterChain(filterChain);
  18. }
  19. }
  20. } else {
  21. // Request dispatcher in use
  22. filterChain = new ApplicationFilterChain();
  23. }
  24. filterChain.setServlet(servlet);
  25. filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
  26. // Acquire the filter mappings for this Context
  27. StandardContext context = (StandardContext) wrapper.getParent();
  28. FilterMap filterMaps[] = context.findFilterMaps();
  29. // If there are no filter mappings, we are done
  30. if ((filterMaps == null) || (filterMaps.length == 0))
  31. return filterChain;
  32. // Acquire the information we will need to match filter mappings
  33. DispatcherType dispatcher =
  34. (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
  35. String requestPath = null;
  36. Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
  37. if (attribute != null){
  38. requestPath = attribute.toString();
  39. }
  40. String servletName = wrapper.getName();
  41. // Add the relevant path-mapped filters to this filter chain
  42. for (FilterMap filterMap : filterMaps) {
  43. if (!matchDispatcher(filterMap, dispatcher)) {
  44. continue;
  45. }
  46. if (!matchFiltersURL(filterMap, requestPath))
  47. continue;
  48. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  49. context.findFilterConfig(filterMap.getFilterName());
  50. if (filterConfig == null) {
  51. // FIXME - log configuration problem
  52. continue;
  53. }
  54. filterChain.addFilter(filterConfig);
  55. }
  56. // Add filters that match on servlet name second
  57. for (FilterMap filterMap : filterMaps) {
  58. if (!matchDispatcher(filterMap, dispatcher)) {
  59. continue;
  60. }
  61. if (!matchFiltersServlet(filterMap, servletName))
  62. continue;
  63. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  64. context.findFilterConfig(filterMap.getFilterName());
  65. if (filterConfig == null) {
  66. // FIXME - log configuration problem
  67. continue;
  68. }
  69. filterChain.addFilter(filterConfig);
  70. }
  71. // Return the completed filter chain
  72. return filterChain;
  73. }

我们需要关注的关键点是这句代码(32行):FilterMap filterMaps[] = context.findFilterMaps();这个数组决定了filter的顺序,这里的context是org.apache.catalina.core.StandardContext,相关的方法有2个,但是这里有关的是addFilterMapBefore(...)。一个FilterMap对应一个Filter,这个方法会被调用多次的

StandardContext#addFilterMap(),StandardContext#addFilterMapBefore()

  1. @Override
  2. public void addFilterMapBefore(FilterMap filterMap) {
  3. validateFilterMap(filterMap);
  4. // Add this filter mapping to our registered set
  5. filterMaps.addBefore(filterMap);
  6. fireContainerEvent("addFilterMap", filterMap);
  7. }

接下来我们需要看看谁在调用addFilterMapBefore(...),是下面方法

org.apache.catalina.core.ApplicationFilterRegistration#addMappingForUrlPatterns(...)。这个方法生成FilterMap并添加,所以关键点是谁调用这个方法以及其调用顺序。

  1. @Override
  2. public void addMappingForUrlPatterns(
  3. EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
  4. String... urlPatterns) {
  5. FilterMap filterMap = new FilterMap();
  6. filterMap.setFilterName(filterDef.getFilterName());
  7. if (dispatcherTypes != null) {
  8. for (DispatcherType dispatcherType : dispatcherTypes) {
  9. filterMap.setDispatcher(dispatcherType.name());
  10. }
  11. }
  12. if (urlPatterns != null) {
  13. // % decoded (if necessary) using UTF-8
  14. for (String urlPattern : urlPatterns) {
  15. filterMap.addURLPattern(urlPattern);
  16. }
  17. if (isMatchAfter) {
  18. context.addFilterMap(filterMap);
  19. } else {
  20. context.addFilterMapBefore(filterMap);
  21. }
  22. }
  23. // else error?
  24. }

找到调用地方如下org.springframework.boot.web.servlet.AbstractFilterRegistrationBean#configure(...),这里就到了springboot控制了,他是衔接springboot和servlet的地方,需要注意下这个衔接点。

  1. @Override
  2. protected void configure(FilterRegistration.Dynamic registration) {
  3. super.configure(registration);
  4. EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
  5. if (dispatcherTypes == null) {
  6. dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
  7. }
  8. Set<String> servletNames = new LinkedHashSet<>();
  9. for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
  10. servletNames.add(servletRegistrationBean.getServletName());
  11. }
  12. servletNames.addAll(this.servletNames);
  13. if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
  14. registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
  15. }
  16. else {
  17. // 这里调用
  18. if (!servletNames.isEmpty()) {
  19. registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
  20. StringUtils.toStringArray(servletNames));
  21. }
  22. if (!this.urlPatterns.isEmpty()) {
  23. registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
  24. StringUtils.toStringArray(this.urlPatterns));
  25. }
  26. }
  27. }

也就是说这个configure(..)方法也会被调用多次,继续追查看看谁在调用他。

org.springframework.boot.web.servlet.DynamicRegistrationBean#register(...)

  1. @Override
  2. protected final void register(String description, ServletContext servletContext) {
  3. D registration = addRegistration(description, servletContext);
  4. if (registration == null) {
  5. logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
  6. return;
  7. }
  8. // 这里调用
  9. configure(registration);
  10. }

接着看调用链org.springframework.boot.web.servlet.RegistrationBean#onStartup(...)

  1. @Override
  2. public final void onStartup(ServletContext servletContext) throws ServletException {
  3. String description = getDescription();
  4. if (!isEnabled()) {
  5. logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
  6. return;
  7. }
  8. register(description, servletContext);
  9. }

接着看调用链org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize(...),这个方法只调用一次,下面的filter通过遍历生成。

  1. private void selfInitialize(ServletContext servletContext) throws ServletException {
  2. prepareWebApplicationContext(servletContext);
  3. registerApplicationScope(servletContext);
  4. WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
  5. for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
  6. // 这里遍历调用
  7. beans.onStartup(servletContext);
  8. }
  9. }

 也就是说关键点在org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getServletContextInitializerBeans()方法上

  1. protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
  2. return new ServletContextInitializerBeans(getBeanFactory());
  3. }

继续追调用org.springframework.boot.web.servlet.ServletContextInitializerBeans构造函数

  1. @SafeVarargs
  2. public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
  3. Class<? extends ServletContextInitializer>... initializerTypes) {
  4. this.initializers = new LinkedMultiValueMap<>();
  5. this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
  6. : Collections.singletonList(ServletContextInitializer.class);
  7. addServletContextInitializerBeans(beanFactory);
  8. addAdaptableBeans(beanFactory);
  9. // 排序就在这里
  10. List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
  11. .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
  12. .collect(Collectors.toList());
  13. this.sortedList = Collections.unmodifiableList(sortedInitializers);
  14. logMappings(this.initializers);
  15. }

所以现在知道这个排序是怎么决定了吧,也即@Order注解,注意,如果这个Bean不是受Spring管理的就不会生效的。不生效的话默认值是2147483647,这样顺序会在最后面。

现在我们顺着来捋一下这个关系,其中有一个关键点需要明确的是,通常情况下,我们springboot应用是servlet容器的,所以实例化的ApplicationContext类型是:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext。有了这个确定的类型,很多链路就变得清晰了。可以看到最后是丢给线程池来处理了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/921901
推荐阅读
相关标签
  

闽ICP备14008679号