本文共 16065 字,大约阅读时间需要 53 分钟。
博主最近刚拿到一个微服务的新项目,边研究边分析从框架基础开始慢慢带领大家研究微服务的一些东西,这次给大家分析下Springboot中的过滤器和拦截器的区别。虽然上次分析过过滤器,但是主要是分析的cas流程,所以就没太深入,大家也可以看一下的啊
cas源码分析:
好的,正题开始:首先讲解一下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     }    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     }    现在才到了添加过滤器最关键的部分,这个部分已经基本把上面的三个问题的答案告诉大家了,详情源码如下:
1 //开始添加过滤器 2     public ServletContextInitializerBeans(ListableBeanFactory beanFactory, 3             Class     ... 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            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     }          一个一个方法分析一下,让大家看个明白到底是怎么回事,为什么这三种方法都可以实现添加过滤器
1 //获取我们的实现FilterRegistrationBean类的过滤器 2     private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) { 3         for (Class      initializerType : this.initializerTypes) { 4             //获取type为ServletContextInitializer的排好序的类,跟是否实现order类无关! 5             for (Entry            initializerBean : getOrderedBeansOfType(beanFactory, 6                     initializerType)) { 7                    //这时候就开始判断实现FilterRegistrationBean类的过滤器 8                 addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory); 9             }10         }11     }          获取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     }    我们再来看一下另一种添加的方法
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           ) listenerType, 9                     new ServletListenerRegistrationBeanAdapter());10         }11     }          其实最后获取出来后,都是进行创建FilterRegistrationBean
1 privatevoid addAsRegistrationBean(ListableBeanFactory beanFactory, Class type, 2 Class beanType, RegistrationBeanAdapter adapter) { 3 //从beanfactory中获取为filter类型的bean 4 List > entries = getOrderedBeansOfType(beanFactory, beanType, this.seen); 5 for (Entry 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 @Override22 public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {23 FilterRegistrationBean bean = new FilterRegistrationBean<>(source);24 bean.setName(name);25 return bean;26 } 
现在已经把过滤器找的了并且已经添加成功了,开始进行注册时调用的是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就是刚才添加进来的过滤器map12             for (Entry            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         }          到这里,过滤器初始化就完成了也把开头的三个问题给大家讲解明白了,剩下的就是过滤请求的过程了,看一下请求过来时的源码处理
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 needed12                 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.doFilter32                             (request.getRequest(), response.getResponse());33                     }34                 }35 36             }    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     }    创建后会进行对请求的过滤,源码:
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 instance42         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 point53             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     }    到此创建以及过滤请求的流程分析也就结束了,关于URL过滤的问题,匹配的时候只会识别斜杠/和*不要以为?后面的参数也算到URL过滤里,匹配完路径就完了,然后和拦截器的创建以及拦截分析做一下对比,分析一下两者的区别,如果不知道拦截器的创建以及流程处理可以看一下我的另一篇文章:
相同点:
差异点:
转载地址:http://amvuz.baihongyu.com/