拦截器和过滤器
参考:
过滤器和拦截器的区别_至今没搞明白的博客-CSDN博客_过滤器和拦截器的区别
拦截器与过滤器的区别_℡tang的博客-CSDN博客_拦截器和过滤器的区别
文章目录
- 拦截器和过滤器
- 过滤器
- 概念
- 作用
- Filter链与Filter生命周期
- SpringBoot 实现过滤器
- 方式一:@WebFilter注解
- 方式二:自定义配置类注入
- 拦截器
- SpringBoot 实现拦截器
- 区别
- 触发时机不同
- 使用场景
过滤器
概念
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生命周期
- init(): 初始化Filter 实例,Filter 的生命周期与 Servlet 是相同的,也就是当 Web 容器(tomcat)启动时,调用 init() 方法初始化实例,Filter只会初始化一次。需要设置初始化参数的时候,可以写到init()方法中。
- doFilter(): 业务处理,拦截要执行的请求,对请求和响应进行处理,一般需要处理的业务操作都在这个方法中实现
- 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {String filter1 = filterConfig.getInitParameter("filter1");System.out.println("==========================filter1 init" + filter1);}@Overridepublic 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);}@Overridepublic 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {String filter1 = filterConfig.getInitParameter("filter1");System.out.println("==========================filter1 init" + filter1);}@Overridepublic 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);}@Overridepublic 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {String filter2 = filterConfig.getInitParameter("filter2");System.out.println("==========================filter2 init" + filter2);}@Overridepublic 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);}@Overridepublic 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*/@Beanpublic FilterRegistrationBean<Filter> myFilter1() {//匹配拦截 URLString 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*/@Beanpublic FilterRegistrationBean<Filter> myFilter2() {//匹配拦截 URLString 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,不会执行*/@Overridepublic 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 参数,可以在此做一些修改动作。*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}/*** 在整个请求处理完成后(包括视图渲染)执行,* 做一些资源的清理工作,这个方法只有在 preHandle(……) 被成功执行后并且返回true才会被执行*/@Overridepublic 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 {@Overridepublic 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。