• 拦截器和过滤器


    拦截器和过滤器

    参考:

    过滤器和拦截器的区别_至今没搞明白的博客-CSDN博客_过滤器和拦截器的区别

    拦截器与过滤器的区别_℡tang的博客-CSDN博客_拦截器和过滤器的区别

    过滤器

    概念

    Filter 过滤器,Servlet规范中三个技术 Servlet Listener Filter(顺序为L F S)

    Filter是sun公司中servlet2.3后增加的一个新功能,在javaEE中定义了一个接口 javax.servlet.Filter来描述过滤器

    作用

    通过Filter可以拦截访问web资源的请求与响应操作,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

    在java web中,针对传入的request,或response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符。

    相当于:一大堆请求中,只要选择符合要求的请求留下。定义这个要求的工具就是过滤器

    (一堆字母中,取一个A)

    Filter是javax.servlet包下的一个接口主要有以下三个方法

    destory()
    doFilter(ServletRequest request,ServletResponse response,FilterCjain chain)
    init(FilterConfig filterConfig)

    Filter链与Filter生命周期

    在这里插入图片描述

    多个Filter对同一个资源进行了拦截,那么当我们在开始的Filter中执行chain.doFilter(request,response)时,是访问下一个Filter,直到最后一个Filter执行时,它后面没有了Filter,才会访问web资源

    那么怎么保证顺序的?

    执行顺序取决于在web.xml文件中配置的先后顺序

    Filter生命周期

    1. init(): 初始化Filter 实例,Filter 的生命周期与 Servlet 是相同的,也就是当 Web 容器(tomcat)启动时,调用 init() 方法初始化实例,Filter只会初始化一次。需要设置初始化参数的时候,可以写到init()方法中。
    2. doFilter(): 业务处理,拦截要执行的请求,对请求和响应进行处理,一般需要处理的业务操作都在这个方法中实现
    3. destroy() : 销毁实例,关闭容器时调用 destroy() 销毁 Filter 的实例。

    过滤器是JavaEE标准,采用函数回调的方式进行。是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理。

    SpringBoot 实现过滤器
    方式一:@WebFilter注解

    通过 @WebFilter 注解,将类声明为 Bean 过滤器类,在启动类添加注解 @ServletComponentScan ,让 Spring 可以扫描到。

    实现javax.servlet.Filter接口,重写三个关键方法

    package com.ung.myflowable.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description: @WebFilter 属性中没有配置顺序的,执行顺序和 Filter 类名称字符排序有关 所以这个需要注意
     */
    @WebFilter(
            filterName = "filter1",//自定义过滤器的名称
            urlPatterns = "/filter1/*"//自定义需要拦截的URL,可以使用正则匹配,若没指定该参数值,则默认拦截所有请求
    )
    public class MyFilter1 implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            String filter1 = filterConfig.getInitParameter("filter1");
            System.out.println("==========================filter1 init" + filter1);
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            //业务处理 获取ip地址
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String remoteAddr = request.getRemoteAddr();
            System.out.println("MyFilter1  访问的ip:" + remoteAddr);
            //放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    
        @Override
        public void destroy() {
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    package com.ung.myflowable;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    /**
     * @author: wenyi
     * @create: 2022/10/18
     * @Description:
     */
    @SpringBootApplication
    @ServletComponentScan//启动类添加注解 @ServletComponentScan 
    public class MyFlowableApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyFlowableApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    方式二:自定义配置类注入

    FilterRegistrationBean对象配置Filter,注解声明Bean,交由 Spring 容器管理

    FilterRegistrationBean.setOrder() 决定 Filter 执行顺序。 数字小的先执行

    过滤器1 MyFilter1

    package com.ung.myflowable.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description: @WebFilter 属性中没有配置顺序的,执行顺序和 Filter 类名称字符排序有关 所以这个需要注意
     */
    //@WebFilter(
    //        filterName = "filter1",//自定义过滤器的名称
    //        urlPatterns = "/filter1/*"//自定义需要拦截的URL,可以使用正则匹配,若没指定该参数值,则默认拦截所有请求
    //)
    public class MyFilter1 implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            String filter1 = filterConfig.getInitParameter("filter1");
            System.out.println("==========================filter1 init" + filter1);
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            //业务处理 获取ip地址
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String remoteAddr = request.getRemoteAddr();
            System.out.println("MyFilter1  访问的ip:" + remoteAddr);
            //放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    
        @Override
        public void destroy() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    过滤器2 MyFilter2

    package com.ung.myflowable.filter;
    
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description:
     */
    public class MyFilter2 implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            String filter2 = filterConfig.getInitParameter("filter2");
            System.out.println("==========================filter2 init" + filter2);
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            //业务处理 获取ip地址
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String remoteAddr = request.getRemoteAddr();
            System.out.println("MyFilter2  访问的ip:" + remoteAddr);
            filterChain.doFilter(servletRequest, servletResponse);
        }
    
        @Override
        public void destroy() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    配置类

    package com.ung.myflowable.config;
    
    import com.ung.myflowable.filter.MyFilter1;
    import com.ung.myflowable.filter.MyFilter2;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description:
     */
    @Configuration
    public class MyFilterConfig {
        /**
         * 注册 过滤器 Filter
         */
        @Bean
        public FilterRegistrationBean<Filter> myFilter1() {
            //匹配拦截 URL
            String urlPatterns = "/myFilter1/*,/system/*";
            FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
            registration.setDispatcherTypes(DispatcherType.REQUEST);
            registration.setFilter(new MyFilter1());
            registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
            //设置名称
            registration.setName("filter1");
            //设置过滤器链执行顺序
            registration.setOrder(1);
            //启动标识
            registration.setEnabled(true);
            //添加初始化参数
            registration.addInitParameter("filter1", "filter1filter1filter1filter1");
            return registration;
        }
        /**
         * 注册 过滤器 Filter
         */
        @Bean
        public FilterRegistrationBean<Filter> myFilter2() {
            //匹配拦截 URL
            String urlPatterns = "/myFilter2/*,/system/*";
            FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
            registration.setDispatcherTypes(DispatcherType.REQUEST);
            registration.setFilter(new MyFilter2());
            registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
            //设置名称
            registration.setName("filter2");
            //设置过滤器链执行顺序
            registration.setOrder(3);
            //启动标识
            registration.setEnabled(true);
            //添加初始化参数
            registration.addInitParameter("filter2", "filter2filter2filter2filter2filter2");
            return registration;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    拦截器

    Interceptor 拦截器是spring容器的,是spring支持的java里的拦截器是动态拦截Action调用的对象,是面向切面编程(AOP,Aspect Oriented Program)的。

    就是在你的Service或者一个方法前调用一个方法,或者在方法后调用一个方法。比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
    相当于:一个流程在进行的时候,干预它的进展,甚至可以终止结束,就是拦截器的工作。

    (一堆字母中,进行干预,验证,做些其他事情)

    SpringBoot 实现拦截器

    1.实现接口HandlerInterceptor 实现拦截器

    2.在WebMvc配置类中配置拦截器,并设置拦截规则

    第一步很好做:

    preHandle: 预先处理,在目标的controller方法执行之前,进行处理

    postHandle: 在目标的controller方法执行之后,到达指定页面之前进行处理

    afterCompletion: 在页面渲染之后进行处理

    package com.ung.myflowable.interceptor;
    
    import com.ung.myflowable.annotation.AbolishInterceptor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description:
     */
    @Slf4j
    public class MyInterceptor implements HandlerInterceptor {
        /**
         * 当url已经匹配到controller层中某个方法时,在方法执行前执行
         * 它会决定是否放行,返回true,放行,返回false,不会执行
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            String methodName = method.getName();
            log.debug("方法名:{}", methodName);
            // 通过方法,可以获取该方法上的自定义注解,然后通过注解来判断该方法是否要被拦截
            // @AbolishInterceptor 是自定义的注解
            AbolishInterceptor annotation = method.getAnnotation(AbolishInterceptor.class);
            log.info("annotation===============,{}", annotation);
            Parameter[] parameters = method.getParameters();
            if (parameters != null) {
                System.out.println("传入的参数name:" + parameters[0]);
            }
            if (annotation != null) {
                return true;
            }
    //        String token = request.getParameter("token");
    //        if (token == null || "".equals(token)) {
    //            log.info("未登录");
    //            return false;
    //        }
            // 返回true才会继续执行,返回false则取消当前请求
            return true;
        }
    
        /**
         * url 匹配到Controller 中的某个方法,且执行完了该方法,
         * 但是在 DispatcherServlet 视图渲染之前执行。在这个方法中有个 ModelAndView 参数,可以在此做一些修改动作。
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 在整个请求处理完成后(包括视图渲染)执行,
         * 做一些资源的清理工作,这个方法只有在 preHandle(……) 被成功执行后并且返回true才会被执行
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    参考:SpringBoot中WebMvcConfigurationSupport与WebMvcConfigurer_LoneWalker、的博客-CSDN博客_webmvcconfigurationsupport和webmvcconfigurer

    springboot2.0后 推荐使用两种方式配置WebMvc

    • 实现WebMvcConfigurer 接口
    • 继承WebMvcConfigurationSupport 类

    区别:

    首先从SpringBoot自动装配来讲,SpringBoot对WebMvc做了自动配置

    可以找到 自动配置类 WebMvcAutoConfiguration

    在这里插入图片描述

    里面蓝色框表示: WebMvcConfigurer 类存在bean容器,WebMvc自动配置生效

    红色框表示:WebMvcConfigurationSupport 类存在bean容器, 自动配置就不生效

    WebMvcConfigurer 配置类是spring内部的配置方式,采用JavaBean的方式代替 xml的方式,可以进行自定义Handler,Interceptor,ViewResolver,MessageConverter

    常用方法

     /* 拦截器配置 */
    void addInterceptors(InterceptorRegistry var1);
    /* 视图跳转控制器 */
    void addViewControllers(ViewControllerRegistry registry);
    /**
         *静态资源处理
    **/
    void addResourceHandlers(ResourceHandlerRegistry registry);
    /* 默认静态资源处理器 */
    void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
    /**
         * 这里配置视图解析器
     **/
    void configureViewResolvers(ViewResolverRegistry registry);
    /* 配置内容裁决的一些选项*/
    void configureContentNegotiation(ContentNegotiationConfigurer configurer);
    /** 解决跨域问题 **/
    public void addCorsMappings(CorsRegistry registry) ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    WebMvcConfigurationSupport 是mvc的基本实现并包含了WebMvcConfigurer接口中的方法

    WebMvcAutoConfiguration 是mvc的自动装配类并部分包含了WebMvcConfigurer接口中的方法

    如果项目没有使用 WebMvcConfigurationSupport 就会使用自动配置类,进行默认配置,比如对静态资源的访问

    如果自动配置类不生效,会怎样?

    WebMvcProperties 和 ResourceProperties 失效

    两个配置类中的属性都在 WebMvcAutoConfiguration 中使用。当WebMvc自动配置失效(WebMvcAutoConfiguration自动化配置)时,会导致无法视图解析器无法解析并返回到对应的视图。

    虽然 Bean容器里面有 WebMvcConfigurationSupport 但是 这个类没有 WebMvcProperties 和 ResourceProperties属性;

    解决:可以实现WebMvcConfigurer并重写相关方法来达到类似的功能

    @EnableWebMvc 注解

    该注解会关闭默认配置

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({DelegatingWebMvcConfiguration.class})
    public @interface EnableWebMvc {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    DelegatingWebMvcConfiguration这个类是继承了WebMvcConfigurationSupport的,这就是它为什么可以关闭默认配置的原因了。

    希望关闭默认配置,自己完全重新实现一个

    @EnableWebMvc
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
    
    • 1
    • 2
    • 3
    • 4

    你希望重写部分配置

    //@EnableWebMvc
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
    
    • 1
    • 2
    • 3
    • 4

    @EnableWebMvc
    @Configuration
    public class WebConfig extends WebMvcAutoConfiguration {
    
    
    • 1
    • 2
    • 3
    • 4

    配置类

    package com.ung.myflowable.config;
    
    import com.ung.myflowable.interceptor.MyInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    
    /**
     * @author: wenyi
     * @create: 2022/10/19
     * @Description: 拦截器配置类
     */
    @Configuration
    public class MyInterceptor2Config implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            System.out.println("添加到容器中啦+++++++++++++++++++++");
            registry.addInterceptor(new MyInterceptor())//添加拦截器
                	.addPathPatterns("/**")//拦截路径
                    .excludePathPatterns("/login", "/static/*");//忽略的拦截路径
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    区别

    ①:拦截器是基于java的反射机制(动态代理)的,而过滤器是基于函数的回调
    ②:拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。
    ③:拦截器只对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
    ④:拦截器可以访问action上下文、值、栈里面的对象,而过滤器不可以。
    ⑤:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
    ⑥:拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

    触发时机不同

    过滤器是请求进入servlet之前进行处理,请求结束返回也是,在servlet处理后,返回给前端前执行,

    过滤器包裹servlet,servlet包裹拦截器

    在这里插入图片描述

    使用场景

    SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

    1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
    2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
    3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
    4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
    5、OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。

  • 相关阅读:
    Qt 学习(四) —— QBoxLayout盒模型布局
    igraph load 无法读取保存的graph attr
    RocketMQ快速入门:namesrv、broker、dashboard的作用及消息发送、消费流程(三)
    java之异常
    三、Typora软件的介绍及安装
    现在的数字藏品该怎么玩才不会被割韭菜?
    Java 华为真题-新学校选址
    【游记】CSP2023-S2
    慢 SQL 分析与优化
    react经典面试题解析
  • 原文地址:https://blog.csdn.net/weixin_46685542/article/details/127425888