首页 > 编程语言 >java拦截Filter和过滤器HandlerInterceptor

java拦截Filter和过滤器HandlerInterceptor

时间:2023-07-23 15:45:09浏览次数:33  
标签:调用 java void request Filter response HandlerInterceptor

什么是过滤器

过滤器Filter是基于Servlet实现,对进入到Servlet的请求拦截。主要用于对字符编码,跨域等问题过滤。如下图:
7eac30666a16453ab87e7357829fca49.png
所有的请求和都经过Filter,通过定义Filter,能够对请求进行编码操作。代码是以接口的形式提供:

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}

Filter有三个方法init()、doFilter()、destroy()方法
init():web容器启动时,web服务器会创建Filter实例对象,并调用init方法,init在创建Filter创建一次。
destroy():web容器销毁时,会调用该方法,在方法内会销毁资源。
doFilter(): 每一次请求过来,都会被调用,并传递到下一个Filter。如果有配置了URL,即会根据匹配的URL进行选择Filter。最后的Filter会调用具体Servlet执行具体的业务。

配置实例化如下:

// 拦截所有的请求,
@Configuration
public class FilterConfig1 {
    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.setName("PiceaFilter");
        registration.addInitParameter("URL","http://localhost:8080");
        registration.setOrder(1);
        return registration;
    }
}

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 相当于是模拟预设工作
        System.out.println("过滤器 这是准备工作.....");
        // 放行【如果没有下面的doFilter过程,即为拦截了】,放行后可以到达servlet
        chain.doFilter(request, response);
        // 相当于是模拟善后工作【servlet工作完后,再回到这里】
        System.out.println("过滤器 这是善后工作.....");
    }
}

什么是拦截器

同Filter一样,对请求做处理的。也同样使用了AOP的思想,当每个请求,请求到具体的Controller之前,会被拦截。
image.png

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

preHandle(): 在每个请求处理之前被调用,用来对请求做一些初始化操作,或者是预处理,判断当前请求是否放行。当返回false则立即返回,当返回true时,请求到下一个HandlerInterceptor的preHandle方法,如果是最后一个,则会调用到Controller的具体请求。
postHandle(): 在处理完每个请求后调用
afterCompletion(): 在DispatcherServlet 渲染视图ModelAndView后调用,在之前前后端不分离的情况下,会经常使用,但前后端分离情况下,已经很少使用了。


@Configuration
public class FilterConfig implements WebMvcConfigurer {

    /**
    * 拦截所有请求,但过滤掉 /login等请求
    */	
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptor = registry.addInterceptor(new LoginFilter());
        interceptor.addPathPatterns("/**").excludePathPatterns("/login","/toLogin","/css/**","/js/**","/kaptcha");
    }
}

拦截器执行流程如下图:
拦截器执行图.png

过滤器和拦截器的区别

  1. 使用范围不一样

过滤器仅是Servlet的实现规范,仅在tomcat等容器中调用,即在web容器中使用
拦截器是Spring中实现,不仅在web容器中使用,可以在Application和Swing程序中。

  1. 拦截时机不一样

image.png
请求过来,先进入到tomcat容器,流转到Filter,具体到Servlet的Service方法,被DispatcherServlet流转到Interceptor链中,最后执行Controller的方法。
如下图:
image.png

  1. 拦截范围不一样

过滤器会拦截所有请求
拦截器仅会拦截Controller的请求和static资源目录下的请求

  1. 适用场景不一样

过滤器场景:字符转码、跨域问题
拦截器场景:权限控制,日志打印,参数校验

Filter过滤器调用源码分析

Filter调用时在Tomcat中实现,其中有容器StandardWrapperValve.invoke()方法中被创建执行。

public final void invoke(Request request, Response response) throws IOException, ServletException {
        boolean unavailable = false;
    	// 省略...

        request.setAttribute("org.apache.catalina.core.DISPATCHER_TYPE", dispatcherType);
        request.setAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH", requestPathMB);
        ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
}

ApplicationFilterFactory.createFilterChain创建FilterChain

public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
        if (servlet == null) {
            return null;
        } else {
            // 1
            ApplicationFilterChain filterChain = null;
            if (request instanceof Request) {
                Request req = (Request)request;
                if (Globals.IS_SECURITY_ENABLED) {
                    filterChain = new ApplicationFilterChain();
                } else {
                    filterChain = (ApplicationFilterChain)req.getFilterChain();
                    if (filterChain == null) {
                        filterChain = new ApplicationFilterChain();
                        req.setFilterChain(filterChain);
                    }
                }
            } else {
                filterChain = new ApplicationFilterChain();
            }
            FilterMap[] filterMaps = context.findFilterMaps();
            // 2
            if (filterMaps != null && filterMaps.length != 0) {
                int var11 = filterMaps.length;
                int var12;
                FilterMap filterMap;
                ApplicationFilterConfig filterConfig;
                // 2.1
                for(var12 = 0; var12 < var11; ++var12) {
                    filterMap = var10[var12];
                    if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }
            	// 2.2
                for(var12 = 0; var12 < var11; ++var12) {
                    filterMap = var10[var12];
                    if (matchDispatcher(filterMap, dispatcher) && matchFiltersServlet(filterMap, servletName)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }
                return filterChain;
            } else {
                // 3
                return filterChain;
            }
        }
    }
  1. 创建ApplicationFilterChain类

  2. 判断Filter是否存在

    2.2 过滤出匹配URL的Filter
    2.3 过滤出匹配Servlet的Filter
    3. Filter不存在,则直接返回filterChain

创建完ApplicationFilterChain后,调用doFilter方法

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    if (Globals.IS_SECURITY_ENABLED) {
        ServletRequest req = request;
        ServletResponse res = response;
        // 调用内部方法
        try {
            AccessController.doPrivileged(() -> {
                this.internalDoFilter(req, res);
                return null;
            });
        } catch (PrivilegedActionException var7) {
        }
    } else {
        this.internalDoFilter(request, response);
    }
}

private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // 1
    	if (this.pos < this.n) {
            // 1.1
            ApplicationFilterConfig filterConfig = this.filters[this.pos++];
            try {
                Filter filter = filterConfig.getFilter();
                if (Globals.IS_SECURITY_ENABLED) {
                } else {
                    // 1.2 
                    filter.doFilter(request, response, this);
                }
            }
        } else {
            // 2
            try {
                if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
                } else {
                    // 2.1
                    this.servlet.service(request, response);
                }
            }
        }
    }
  1. 当前Filter的pos小于Filters.length的长度

1.1 pos的位置+1
1.2 调用Filter的doFilter方法
2. filter调用完成或者无Filter时
2.1 调用servlet.service方法
分析完Filter的调用源码后,得到是Filter使用了责任链的模型,多个Filter则进行传递,直到最后一个,然后调用Servlet.service方法。

HandlerInterceptor拦截器调用源码分析

拦截器在Spring中DispatcherServlet的doDispatch中有具体实现

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                   // 1
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    // 2
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                	// 3
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                	// 4
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                // 5
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } 
        } finally {
        }
    }
  1. 获取HandlerExecutionChain对象,该对象封装了HandlerInteceptor的调用方法。

  2. 执行HandlerInterceptor的PreHandle方法

  3. 执行具体Controller下的服务

  4. 执行HandlerInterceptor的PostHandle方法

  5. 执行HandlerInterceptor的AfterCompletion方法

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 遍历interceptor方法,如果某个interceptor返回false,则直接调用interceptor的
    // AfterCompletion方法
    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            this.triggerAfterCompletion(request, response, (Exception)null);
            return false;
        }
    }

    return true;
}

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for(int i = this.interceptorIndex; i >= 0; --i) {
        // 从最后一个HandlerInterceptor执行AfterCompletion方法
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.afterCompletion(request, response, this.handler, ex);
    }

}

// 从最后一个HandlerInterceptor执行postHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }

}

标签:调用,java,void,request,Filter,response,HandlerInterceptor
From: https://www.cnblogs.com/skyice/p/17575040.html

相关文章

  • java log level
    Java日志级别的实现简介Java日志级别是一个非常常用的功能,用于控制不同级别的日志输出。在开发过程中,合理设置日志级别可以帮助我们更好地定位和解决问题。本篇文章将介绍如何实现Java日志级别,并提供代码示例和注释说明,帮助刚入行的小白快速学习和掌握这一重要的开发技能。实现......
  • java spring 异步
    JavaSpring异步实现指南引言在开发过程中,我们经常会遇到一些需要长时间处理的操作,例如网络请求、数据库访问等。为了提高程序的性能和响应速度,我们可以使用异步操作来处理这些耗时的任务。在JavaSpring框架中,提供了多种方式来实现异步操作,本文将介绍如何使用JavaSpring来实现......
  • java list转linkedHashMap
    JavaList转LinkedHashMap在Java编程中,我们经常会遇到需要将一个List转换为LinkedHashMap的场景。List是一个有序的集合,而LinkedHashMap是一个有序的键值对集合,它可以保持插入顺序。这种转换可以帮助我们在处理数据时更方便地按照特定的顺序进行操作。使用Java的StreamAPI进行Li......
  • java split 第一个字符分隔
    Java中的split方法及字符分隔的应用在Java编程中,我们经常需要对字符串进行处理和分割。其中,split()方法是一个非常常用的方法,用于将一个字符串分割成一个字符串数组。本文将介绍split()方法的用法,并以第一个字符分隔为例进行详细讲解。split()方法的用法split()方法是Java中的一......
  • java list每一项添加单引号
    JavaList每一项添加单引号在Java中,List是一种常用的集合类,它可以用来存储多个元素。有时候我们会遇到需要在List的每一项前后添加单引号的需求,本文将介绍如何实现这一功能。为什么需要添加单引号在某些场景下,我们可能需要将List中的每一项转化为字符串,并在其前后添加单引号。这......
  • java spark-core wordcount
    实现JavaSpark-CoreWordCount流程概述下面是实现JavaSpark-CoreWordCount的整体流程:步骤描述1.创建SparkConf创建一个SparkConf对象,设置应用程序的名称和运行模式2.创建JavaSparkContext创建一个JavaSparkContext对象,用于连接Spark集群3.加载文本文件......
  • java list 深拷贝
    JavaList深拷贝的实现方法概述在Java开发中,List是一个常用的集合类型,它可以存储多个元素。有时我们需要对List进行复制,得到一个全新的副本,即深拷贝。本文将介绍如何在Java中实现List的深拷贝。流程下面是实现JavaList深拷贝的步骤:步骤操作1创建一个新的List对象......
  • java 设置时区
    Java设置时区在Java中,可以使用java.util.TimeZone类来设置时区。时区在处理日期和时间时非常重要,因为不同的地区可能有不同的标准时间。本文将介绍如何使用Java设置时区并提供一些代码示例。什么是时区?时区是指地球上划分为不同区域的标准时间。由于地球自转和地理位置的差异,每......
  • java 文件成快捷方式
    如何将Java文件创建为快捷方式简介在Java开发中,我们经常会遇到需要创建快捷方式的需求。快捷方式是指在操作系统中创建一个指向特定文件或目录的链接,方便用户快速访问。本文将介绍如何使用Java代码创建一个快捷方式。步骤概览下面是用于创建Java文件快捷方式的步骤概览:步骤......
  • java 上传文件到服务器
    Java上传文件到服务器在开发Web应用程序时,经常会遇到需要上传文件到服务器的需求。Java提供了多种方法来实现文件上传功能。本文将介绍一种基于Java的文件上传方法,并提供详细的代码示例。1.前提条件在开始之前,确保已经具备以下环境和工具:JDK1.8或更高版本Eclipse或其他Java......