zoukankan      html  css  js  c++  java
  • 源码分析过滤器与拦截器的区别

     

    博主最近刚拿到一个微服务的新项目,边研究边分析从框架基础开始慢慢带领大家研究微服务的一些东西,这次给大家分析下Springboot中的过滤器和拦截器的区别。虽然上次分析过过滤器,但是主要是分析的cas流程,所以就没太深入,大家也可以看一下的啊

    cas源码分析:https://www.cnblogs.com/guoxiaoyu/p/13280259.html

    好的,正题开始:首先讲解一下Springboot中如何进行添加过滤器、进行过滤器过滤请求。添加示例必须来一下

     1 @Configuration
     2 public class WebConfiguration{
     3 
     4 @Bean
     5     public FilterRegistrationBean testFilterByMe(){
     6         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
     7         filterRegistrationBean.setFilter(new TestFilterByMe());
     8         filterRegistrationBean.setOrder(1);
     9         return filterRegistrationBean;
    10     }
    11 }
    过滤器示例

      我们过滤器为什么要添加到FilterRegistrationBean中,不添加可不可以,为什么用@WebFilter注解也可以呢,用@Component可不可以以的呢?博主今天就通过源码给大家讲解一下这几个问题

      首先我们的Springboot开始启动后,会进行创建bean和web服务器tomcat,源码附上:

     1 @Override
     2     protected void onRefresh() {
     3     //onRefresh方法就是扫描包,解析配置类的过程,原生spring中是一个空方法,这里进行重写用于创建tomcat服务器
     4         super.onRefresh();
     5         try {
     6             //开始创建web服务器tomcat,所以Springboot才可以不依赖web容器,自己就可以启动成功并进行访问
     7             createWebServer();
     8         }
     9         catch (Throwable ex) {
    10             throw new ApplicationContextException("Unable to start web server", ex);
    11         }
    12     }
    org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext

      createWebServer()这个方法的源码我就不贴上了,大家可以自己看一下源码,最后就会看到new tomcat();并进行启动tomcat。启动容器后当然是开始进行初始化。

    1 private void selfInitialize(ServletContext servletContext) throws ServletException {
    2         prepareWebApplicationContext(servletContext);
    3         registerApplicationScope(servletContext);
    4         WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    5         //getServletContextInitializerBeans()这个方法进开始进行解析并添加filter过滤器 了
    6         for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    7             beans.onStartup(servletContext);
    8         }
    9     }
    org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext

      现在才到了添加过滤器最关键的部分,这个部分已经基本把上面的三个问题的答案告诉大家了,详情源码如下:

     1 //开始添加过滤器
     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         //这里实现的添加形式是通过FilterRegistrationBean类型注册的
     8         addServletContextInitializerBeans(beanFactory);
     9         //这里是通过beanfactory中获取filter类型过滤器后添加进来的,这就明白了,只要让spring扫描到,
    10         //过滤器自己实现了filter接口,你就会给添加到过滤器链
    11         addAdaptableBeans(beanFactory);
    12         //都会添加到initializers这一个map中
    13         List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
    14                 .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
    15                 .collect(Collectors.toList());
    16         this.sortedList = Collections.unmodifiableList(sortedInitializers);
    17         logMappings(this.initializers);
    18     }
    org/springframework/boot/web/servlet/ServletContextInitializerBeans

      一个一个方法分析一下,让大家看个明白到底是怎么回事,为什么这三种方法都可以实现添加过滤器

     1 //获取我们的实现FilterRegistrationBean类的过滤器
     2     private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
     3         for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
     4             //获取type为ServletContextInitializer的排好序的类,跟是否实现order类无关!
     5             for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
     6                     initializerType)) {
     7                    //这时候就开始判断实现FilterRegistrationBean类的过滤器
     8                 addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
     9             }
    10         }
    11     }
    org/springframework/boot/web/servlet/ServletContextInitializerBeans.java

    获取bean时debug,观察一下,最后会筛选出来我们FilterRegistrationBean的过滤器,为什么呢?因为这个类的上级实现了ServletContextInitializer

       再来看一下添加的过程,就知道filter要注册到FilterRegistrationBean中的原因了,

     1 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
     2             ListableBeanFactory beanFactory) {
     3         if (initializer instanceof ServletRegistrationBean) {
     4             Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
     5             addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
     6         }
     7         //在这里进行的添加的过程
     8         else if (initializer instanceof FilterRegistrationBean) {
     9             Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
    10             addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
    11         }
    12         else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
    13             String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
    14             addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
    15         }
    16         else if (initializer instanceof ServletListenerRegistrationBean) {
    17             EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
    18             addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
    19         }
    20         else {
    21             addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
    22                     initializer);
    23         }
    24     }
    org/springframework/boot/web/servlet/ServletContextInitializerBeans

      我们再来看一下另一种添加的方法

     1 //另一种添加过滤器的方法在这里
     2     protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
     3         MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
     4         addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
     5         //从bean工厂中获取为Filter类型的类,所以只要我们把我们已经实现Filter接口的类交给spring,beanFactory中有我们的类就可以实现
     6         addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
     7         for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
     8             addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
     9                     new ServletListenerRegistrationBeanAdapter());
    10         }
    11     }
    org/springframework/boot/web/servlet/ServletContextInitializerBeans

      其实最后获取出来后,都是进行创建FilterRegistrationBean

     1 private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
     2             Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
     3             //从beanfactory中获取为filter类型的bean
     4         List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
     5         for (Entry<String, B> entry : entries) {
     6             String beanName = entry.getKey();
     7             B bean = entry.getValue();
     8             if (this.seen.add(bean)) {
     9                 //剩下其他自动实现的创建过程,也是创建一个FilterRegistrationBean并返回
    10                 RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
    11                 int order = getOrder(bean);
    12                 registration.setOrder(order);
    13                 this.initializers.add(type, registration);
    14                 if (logger.isTraceEnabled()) {
    15                     logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
    16                             + order + ", resource=" + getResourceDescription(beanName, beanFactory));
    17                 }
    18             }
    19         }
    20     }
    21         @Override
    22         public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
    23             FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
    24             bean.setName(name);
    25             return bean;
    26         }
    org/springframework/boot/web/servlet/ServletContextInitializerBeans

       现在已经把过滤器找的了并且已经添加成功了,开始进行注册时调用的是onstartup方法,注册到filterDefs这个map中,下面初始化会用到

       这里开始进行过滤器的初始化,new ApplicationFilterConfig方法就需要大家自己去debug了,至少加深一下印象,里面会进行初始化,调用init方法

     1 //开始过滤器的初始化
     2     public boolean filterStart() {
     3 
     4         if (getLogger().isDebugEnabled()) {
     5             getLogger().debug("Starting filters");
     6         }
     7         // Instantiate and record a FilterConfig for each defined filter
     8         boolean ok = true;
     9         synchronized (filterConfigs) {
    10             filterConfigs.clear();
    11             //filterDefs这个map就是刚才添加进来的过滤器map
    12             for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
    13                 String name = entry.getKey();
    14                 if (getLogger().isDebugEnabled()) {
    15                     getLogger().debug(" Starting filter '" + name + "'");
    16                 }
    17                 try {
    18                 //在这里会进行fileter的init方法
    19                     ApplicationFilterConfig filterConfig =
    20                             new ApplicationFilterConfig(this, entry.getValue());
    21                     filterConfigs.put(name, filterConfig);
    22                 } catch (Throwable t) {
    23                     t = ExceptionUtils.unwrapInvocationTargetException(t);
    24                     ExceptionUtils.handleThrowable(t);
    25                     getLogger().error(sm.getString(
    26                             "standardContext.filterStart", name), t);
    27                     ok = false;
    28                 }
    29             }
    30         }
    org/apache/catalina/core/StandardContext

      到这里,过滤器初始化就完成了也把开头的三个问题给大家讲解明白了,剩下的就是过滤请求的过程了,看一下请求过来时的源码处理

     1 // Create the filter chain for this request
     2         //当有请求过来时,首先会调用过滤器,进行过滤,这里会进行过滤器数组的创建
     3         ApplicationFilterChain filterChain =
     4                 ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
     5 
     6         // Call the filter chain for this request
     7         // NOTE: This also calls the servlet's service() method
     8         Container container = this.container;
     9         try {
    10             if ((servlet != null) && (filterChain != null)) {
    11                 // Swallow output if needed
    12                 if (context.getSwallowOutput()) {
    13                     try {
    14                         SystemLogHandler.startCapture();
    15                         if (request.isAsyncDispatching()) {
    16                             request.getAsyncContextInternal().doInternalDispatch();
    17                         } else {
    18                             filterChain.doFilter(request.getRequest(),
    19                                     response.getResponse());
    20                         }
    21                     } finally {
    22                         String log = SystemLogHandler.stopCapture();
    23                         if (log != null && log.length() > 0) {
    24                             context.getLogger().info(log);
    25                         }
    26                     }
    27                 } else {
    28                     if (request.isAsyncDispatching()) {
    29                         request.getAsyncContextInternal().doInternalDispatch();
    30                     } else {
    31                         filterChain.doFilter
    32                             (request.getRequest(), response.getResponse());
    33                     }
    34                 }
    35 
    36             }
    org/apache/catalina/core/StandardWrapperValve
     1 //数组结构可以在这里查看
     2     void addFilter(ApplicationFilterConfig filterConfig) {
     3 
     4         // Prevent the same filter being added multiple times
     5         for(ApplicationFilterConfig filter:filters)
     6             if(filter==filterConfig)
     7                 return;
     8 
     9         if (n == filters.length) {
    10             ApplicationFilterConfig[] newFilters =
    11                 new ApplicationFilterConfig[n + INCREMENT];
    12             System.arraycopy(filters, 0, newFilters, 0, n);
    13             filters = newFilters;
    14         }
    15         filters[n++] = filterConfig;
    16 
    17     }
    ApplicationFilter数据结构

      创建后会进行对请求的过滤,源码:

     1 //过滤器开始过滤
     2 private void internalDoFilter(ServletRequest request,
     3                                   ServletResponse response)
     4         throws IOException, ServletException {
     5 
     6         // Call the next filter if there is one
     7         //过滤器数组大小
     8         if (pos < n) {
     9         //每调用一次都会从数组中自增1 pos++
    10             ApplicationFilterConfig filterConfig = filters[pos++];
    11             try {
    12                 Filter filter = filterConfig.getFilter();
    13 
    14                 if (request.isAsyncSupported() && "false".equalsIgnoreCase(
    15                         filterConfig.getFilterDef().getAsyncSupported())) {
    16                     request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
    17                 }
    18                 if( Globals.IS_SECURITY_ENABLED ) {
    19                     final ServletRequest req = request;
    20                     final ServletResponse res = response;
    21                     Principal principal =
    22                         ((HttpServletRequest) req).getUserPrincipal();
    23 
    24                     Object[] args = new Object[]{req, res, this};
    25                     SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
    26                 } else {
    27                     //每次都会调用doFilter方法,在doFilter方法中调用internalDoFilter,就是一直回调,直到所有过滤器走完
    28                     filter.doFilter(request, response, this);
    29                 }
    30             } catch (IOException | ServletException | RuntimeException e) {
    31                 throw e;
    32             } catch (Throwable e) {
    33                 e = ExceptionUtils.unwrapInvocationTargetException(e);
    34                 ExceptionUtils.handleThrowable(e);
    35                 throw new ServletException(sm.getString("filterChain.filter"), e);
    36             }
    37             //当有过滤器直接返回,并没有继续回调时,回直接return,不会处理该请求,就是下面的步骤
    38             return;
    39         }
    40         //当所有过滤器走完后,将会处理请求
    41         // We fell off the end of the chain -- call the servlet instance
    42         try {
    43             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
    44                 lastServicedRequest.set(request);
    45                 lastServicedResponse.set(response);
    46             }
    47 
    48             if (request.isAsyncSupported() && !servletSupportsAsync) {
    49                 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
    50                         Boolean.FALSE);
    51             }
    52             // Use potentially wrapped request from this point
    53             if ((request instanceof HttpServletRequest) &&
    54                     (response instanceof HttpServletResponse) &&
    55                     Globals.IS_SECURITY_ENABLED ) {
    56                 final ServletRequest req = request;
    57                 final ServletResponse res = response;
    58                 Principal principal =
    59                     ((HttpServletRequest) req).getUserPrincipal();
    60                 Object[] args = new Object[]{req, res};
    61                 SecurityUtil.doAsPrivilege("service",
    62                                            servlet,
    63                                            classTypeUsedInService,
    64                                            args,
    65                                            principal);
    66             } else {
    67             //就是这里直接调用dsipatcherservlet的service方法去转发doget,dopost方法的,
    68             //剩下的就是拦截器的知识点了:
    69                 servlet.service(request, response);
    70             }
    71         } catch (IOException | ServletException | RuntimeException e) {
    72             throw e;
    73         } catch (Throwable e) {
    74             e = ExceptionUtils.unwrapInvocationTargetException(e);
    75             ExceptionUtils.handleThrowable(e);
    76             throw new ServletException(sm.getString("filterChain.servlet"), e);
    77         } finally {
    78             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
    79                 lastServicedRequest.set(null);
    80                 lastServicedResponse.set(null);
    81             }
    82         }
    83     }
    org/apache/catalina/core/ApplicationFilterChain

      到此创建以及过滤请求的流程分析也就结束了,和拦截器的创建以及拦截分析做一下对比,分析一下两者的区别,如果不知道拦截器的创建以及流程处理可以看一下我的另一篇文章:https://www.cnblogs.com/guoxiaoyu/p/13402861.html

    相同点:

    1. 都需要交给spring进行管理,虽然filter本身是servlet,但是如果不给spring管理,根本不会添加成功,也不会过滤请求
    2. 都会在请求真正被处理前进行拦截过滤,如果不符合条件会直接返回,不会处理请求
    3. 两者都可以指定执行顺序

    差异点:

    1. 过滤器先注册,拦截器后注册
    2. 过滤器先执行,拦截器后执行,拦截器可以在请求执行后继续处理其他事情,过滤器只有一个过滤的方法
    3. 过滤器执行时是基于函数回调,而拦截器执行是直接从数组中获取,一个一个执行,作者没有看到哪里用到了反射,网上好多说是反射,拦截器的三个方法都是从数组中获取然后一个一个调用方法进行的,只有在处理请求的时候才用到了invoke反射

  • 相关阅读:
    Flutter动画(1)动画基础介绍(重要)
    flutter AnimationController动画1
    flutter 动画示例
    Nginx 搭建图片服务器
    dart中list的map方法获取index
    Flutter 视频缩略图
    Flutter:设置字体不随系统字体大小进行改变
    Flutter 屏幕适配flutter_screenutil使用心得
    flutter 软键盘弹起导致定位底部按钮浮动在键盘上的问题
    C++ 代码小技巧(一)
  • 原文地址:https://www.cnblogs.com/guoxiaoyu/p/13447666.html
Copyright © 2011-2022 走看看