【JavaWeb】过滤器、拦截器、ControllerAdvice、AOP的联系与区别

过滤器、拦截器、ControllerAdvice、AOP的联系与区别

1. 技术概述

1.1 过滤器

(1) 概述

​ 过滤器 Filter 是JavaWeb(Sevlet、Filter、Listener)三大组件之一,会将浏览器对服务器的资源请求先统一拦截,需要通过Filter才能访问到对应资源,访问操作结束后会回到过滤器再响应给浏览器。

image-20231129212537503

简单总结:

  • 来自J2EE中的Servlet技术
  • 实现原理:基于servlet的函数回调实现
  • 只可以获取到请求中的request和response,无法获取到响应方法的信息
  • 可以拦截所有请求
  • 支持使用xml配置和注解配置

(2) 使用流程

  1. 实现 Filter 接口,重写 doFilter 方法;

  2. 放行请求时调用chain.doFilter()方法;

  3. 启用过滤器,有三种方式,

    第一种是比较原始的xml配置(较为繁琐)

​ 第二种是使用注解 @WebFilter() ,并在启动类上添加@ServletComponentScan注解使用。

​ 第三种是直接使用@Component注解,这样的话@WebFilter配置的路径会失效,因为@WebFilter根本就没生效。

  1. 设置拦截路径,就是要拦截的那个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路由映射、业务处理以及视图响应等流程

image-20231129220034399

1.2 拦截器

(1) 概述

​ 由Spring提供支持的一种动态拦截方法调用的机制,只对action请求起作用(个人理解就是只对SpringMVC的请求访问进行拦截)并且可以获得这些action请求的上下文信息,如参数,返回值等。

image-20231129221215231

  • 来自Spring,不依赖于servlet容器,但依赖于Spring
  • 实现原理:通过反射机制实现
  • 可以获取到Spring中存在的Bean,通过注入的方式
  • 只对action请求起作用,并可以获取到action请求的上下文

(2) 使用流程

  1. 实现 HandlerInterceptor 接口 或 继承 HandlerInterceptorAdapter 类,建议使用接口;
  2. 实现 preHandle 方法(在处理请求前运行),实现 postHandle 方法(在处理请求完毕后运行);
  3. 再新建一个类,继承 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方法。

image-20231129222355800

image-20231129221619431

1.3 @ControllerAdvice

1.4 AOP

2. 四者执行顺序

image-20231129222630462

Filte > 拦截器 > ControllerAdvice > AOP

3. 区别与应用场景总结

roller方法。

[外链图片转存中…(img-ihLRmE8W-1701268496160)]

[外链图片转存中…(img-w0sPrZst-1701268496161)]

1.3 @ControllerAdvice

1.4 AOP

2. 四者执行顺序

[外链图片转存中…(img-KTmihv7D-1701268496161)]

Filte > 拦截器 > ControllerAdvice > AOP

3. 区别与应用场景总结

image-20231129222739645

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值