博主最近刚拿到一个微服务的新项目,边研究边分析从框架基础开始慢慢带领大家研究微服务的一些东西,这次给大家分析下Springboot中的过滤器和拦截器的区别。虽然上次分析过过滤器,但是主要是分析的cas流程,所以就没太深入,大家也可以看一下的啊
cas源码分析:www.cnblogs.com/guoxiaoyu/p...
好的,正题开始:首先讲解一下Springboot中如何进行添加过滤器、进行过滤器过滤请求。添加示例必须来一下
java
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,源码附上:
typescript
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。启动容器后当然是开始进行初始化。
scss
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
现在才到了添加过滤器最关键的部分,这个部分已经基本把上面的三个问题的答案告诉大家了,详情源码如下:
scss
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
一个一个方法分析一下,让大家看个明白到底是怎么回事,为什么这三种方法都可以实现添加过滤器
typescript
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中的原因了,
scss
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
我们再来看一下另一种添加的方法
scss
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
typescript
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方法
scss
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
到这里,过滤器初始化就完成了也把开头的三个问题给大家讲解明白了,剩下的就是过滤请求的过程了,看一下请求过来时的源码处理
scss
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
ini
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数据结构
创建后会进行对请求的过滤,源码:
scss
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
到此创建以及过滤请求的流程分析也就结束了,关于URL过滤的问题,匹配的时候只会识别斜杠/和*不要以为?后面的参数也算到URL过滤里,匹配完路径就完了,然后和拦截器的创建以及拦截分析做一下对比,分析一下两者的区别,如果不知道拦截器的创建以及流程处理可以看一下我的另一篇文章:www.cnblogs.com/guoxiaoyu/p...
相同点:
- 都需要交给spring进行管理,虽然filter本身是servlet,但是如果不给spring管理,根本不会添加成功,也不会过滤请求
- 都会在请求真正被处理前进行拦截过滤,如果不符合条件会直接返回,不会处理请求
- 两者都可以指定执行顺序
差异点:
- 过滤器先注册,拦截器后注册
- 过滤器先执行,拦截器后执行,拦截器可以在请求执行后继续处理其他事情,过滤器只有一个过滤的方法
- 过滤器执行时是基于函数回调,而拦截器执行是直接从数组中获取,一个一个执行,作者没有看到哪里用到了反射,网上好多说是反射,拦截器的三个方法都是从数组中获取然后一个一个调用方法进行的,只有在处理请求的时候才用到了invoke反射