拦截器和过滤器

拦截器和过滤器

参考:

过滤器和拦截器的区别_至今没搞明白的博客-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() {
    }
}

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);
    }
}
方式二:自定义配置类注入

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() {
    }
}

过滤器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() {
    }
}

配置类

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;
    }
}

拦截器

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);
    }
}

参考: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) ;

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 {
}

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

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

@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

你希望重写部分配置

//@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcAutoConfiguration {

配置类

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/*");//忽略的拦截路径

    }
}

区别

①:拦截器是基于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。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值