过滤器、拦截器、ControllerAdvice、AOP的联系与区别
1. 技术概述
1.1 过滤器
(1) 概述
过滤器 Filter 是JavaWeb(Sevlet、Filter、Listener)三大组件之一,会将浏览器对服务器的资源请求先统一拦截,需要通过Filter才能访问到对应资源,访问操作结束后会回到过滤器再响应给浏览器。
简单总结:
- 来自J2EE中的Servlet技术
- 实现原理:基于servlet的函数回调实现
- 只可以获取到请求中的request和response,无法获取到响应方法的信息
- 可以拦截所有请求
- 支持使用xml配置和注解配置
(2) 使用流程
-
实现
Filter
接口,重写doFilter
方法; -
放行请求时调用
chain.doFilter()
方法; -
启用过滤器,有三种方式,
第一种是比较原始的xml配置(较为繁琐)
第二种是使用注解 @WebFilter()
,并在启动类上添加@ServletComponentScan
注解使用。
第三种是直接使用@Component
注解,这样的话@WebFilter配置的路径会失效,因为@WebFilter根本就没生效。
- 设置拦截路径,就是要拦截的那个url路径。
方式二示例:
// 1. 实现Filter接口,通过@WebFilter可以配置过滤范围、name等参数
@WebFilter(value = "/device/*",filterName = "myFilter",displayName = "thisIsFilter")
public class MyFilter implements Filter {
// 销毁过滤器时调用的方法(默认不用实现)
@Override
public void destroy() {
Filter.super.destroy();
}
// 初始化方法(默认不用实现该方法)
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 请求接口前操作
// 调用接口方法前需要执行的代码
filterChain.doFilter(servletRequest,servletResponse); // 放行到下一个过滤器
// 请求接口后操作
// 调用接口方法后需要执行的代码
}
}
====
// 2.
@ServletComponentScan // 如果不标准该注解将Filter将不会生效
@SpringBootApplication
public class DisplayCoreApplication {
public static void main(String[] args) {
SpringApplication.run(DisplayCoreApplication.class, args);
}
}
方式三示例:
// 1.实现Filter接口
public class MyFilter implements Filter {
// 销毁过滤器时调用的方法(默认不用实现)
@Override
public void destroy() {
Filter.super.destroy();
}
// 初始化方法(默认不用实现该方法)
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 请求接口前操作
// 调用接口方法前需要执行的代码
filterChain.doFilter(servletRequest,servletResponse); // 放行到下一个过滤器
// 请求接口后操作
// 调用接口方法后需要执行的代码
}
}
======
// 2. 创建Filter配置类,对自定义的Filter进行定义配置
@Configuration
public class FilterConfig {
/**
* 配置一个Filter注册器
*
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean1() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter1());
registrationBean.setName("filter1");
//设置顺序 order越小,优先级越高
registrationBean.setOrder(10);
return registrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean2() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter2());
registrationBean.setName("filter2");
//设置顺序
registrationBean.setOrder(3);
return registrationBean;
}
@Bean
public Filter filter1() {
return new MyFilter();
}
@Bean
public Filter filter2() {
return new MyFilter2();
}
}
(3) 实现原理
在我们自定义的过滤器中都会实现一个 doFilter()方法,这个方法有一个FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。
// 源码
public final class ApplicationFilterChain implements FilterChain {
//
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private Servlet servlet;
public void doFilter(ServletRequest request, ServletResponse response) {
internalDoFilter(request, response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response) {
if (this.pos < this.n) { //获取第pos个filter
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
} else {
this.servlet.service(request, response);
}
}
}
内部定义了ApplicationFilterConfig[] filters 过滤器配置列表,每一个ApplicationFilterConfig内部持有一个Filter实例,另一个比较重要的是Servlet,实例化后对应原生HttpServlet或SpringMVC的DispatcherServlet,当拦截器链路执行完成后,会调用Servlet中service方法做后续的Url路由映射、业务处理以及视图响应等流程
1.2 拦截器
(1) 概述
由Spring提供支持的一种动态拦截方法调用的机制,只对action请求起作用(个人理解就是只对SpringMVC的请求访问进行拦截)并且可以获得这些action请求的上下文信息,如参数,返回值等。
- 来自Spring,不依赖于servlet容器,但依赖于Spring
- 实现原理:通过反射机制实现
- 可以获取到Spring中存在的Bean,通过注入的方式
- 只对action请求起作用,并可以获取到action请求的上下文
(2) 使用流程
- 实现 HandlerInterceptor 接口 或 继承 HandlerInterceptorAdapter 类,建议使用接口;
- 实现 preHandle 方法(在处理请求前运行),实现 postHandle 方法(在处理请求完毕后运行);
- 再新建一个类,继承 WebMvcConfigurer 接口,再实现addInterceptors方法,并在方法中注册该拦截器、配置拦截路径(不配置默认拦截所有请求);
示例:
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override // 目标资源方法执行前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("before interceptor");
return true; // true 为放行, flase为不放行
}
@Override //目标资源方法执行后执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
log.info("after interceptor");
}
@Override //试图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
====
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor())
//配置拦截规则 addPathPatterns()配置拦截的请求 excludePathPatterns("/login") 配置放行的请求
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
@Bean
public HandlerInterceptor handlerInterceptor() {
return new MyInterceptor();
}
}
(3) 原理浅析
浏览器发出的请求会先被过滤器拦截,然后进入到spring环境,tomcat不识别controller,但是识别servlet,spring的web环境提供了一个非常核心的servlet——DispatchServlet。由DispatchServlet转给Controller,在这之前会先被拦截器拦截执行preHandle方法,根据其返回值决定是否执行Controller方法。
1.3 @ControllerAdvice
1.4 AOP
2. 四者执行顺序
Filte > 拦截器 > ControllerAdvice > AOP
3. 区别与应用场景总结
roller方法。
[外链图片转存中…(img-ihLRmE8W-1701268496160)]
[外链图片转存中…(img-w0sPrZst-1701268496161)]
1.3 @ControllerAdvice
1.4 AOP
2. 四者执行顺序
[外链图片转存中…(img-KTmihv7D-1701268496161)]
Filte > 拦截器 > ControllerAdvice > AOP