拦截器Interceptor和过滤器Filter类似,是面向切面编程的一种具体实现。可以使用拦截器执行某些任务,比如在控制器处理请求前记录日志、更新配置等。

在Spring中,当请求发送到控制器被控制器处理前,必须经过拦截器。

   

过滤器(Filter)

http://www.imooc.com/article/266360?block_id=tuijian_wz

过滤器是工作在容器(比如tomcat)和servlet两者之间,即请求到达servlet之前,或者servlet返回响应给容器之前。

简单说过滤器是工作在请求和服务A之间。

 

HandlerInterceptor

https://www.cnblogs.com/lostyears/p/10706774.html

https://www.jianshu.com/p/4d2aa3d9e8ce

如果拦截器实现HandlerInterceptor,效果同上过滤器。不能实现目标。

比过滤器的优点在于拦截器有handler这个参数可以了解到针对哪个具体的方法进行了拦截。

@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

HandlerInterceptor比MethodInterceptor先执行,只拦截action请求。

 

拦截器Interceptor和过滤器Filter的区别

作用范围

过滤器作用于整个Web应用,可以过滤所有请求和响应。它是Servlet规范的一部分,由Servlet容器管理。

拦截器通常作用于特定框架。比如在Spring Boot中,它主要拦截特定框架的请求处理流程,并在特定框架内处理请求。

实现方式

过滤器实现javax.servlet.Filter接口,并在web.xml或通过注解进行配置。需要实现init()、doFilter()、destroy()等方法。

拦截器在不同框架中有不同实现方式。比如在Spring MVC中,可以实现HandlerInterceptor接口或继承HandlerInterceptorAdapter类。

需要实现preHandler()、postHandler()、afterCompletion()等方法。

拦截器的作用

登录验证和访问控制

用于检查用户的登录状态和权限,并根据需要执行相关处理。比如如果未登录、则重定向到登录页面或返回相应的错误信息。

异常处理和统一错误处理

拦截器可以捕获并处理请求处理过程中发生的异常。比如返回自定义错误页面或错误信息。

过滤器Filter、拦截器Interceptor和切片AOP的区别和调用顺序

过滤器:可以拿到原始的HTTP请求和响应信息,但是拿不到具体处理请求的方法值信息。

拦截器:既可以拿到HTTP请求和响应信息,也可以拿到请求的方法信息,但是拿不到方法调用的参数值信息。 

切片:可以拿到请求方法的传入参数值,拿不到原始的HTTP请求和响应的对象。

简单地说,过滤器和拦截器是针对于拦截HTTP请求和响应的技术,而切片是相对于拦截类和方法的技术。


发起一个请求,若三者共存的情况下:

正常运行顺序为:Filter-Interceptor-ControllerAdvice-Aspect-Controller

异常情况下:Controller-Aspect-ControllerAdvice-Interceptor-Filter

 

 

需求

类似链路追踪,请求服务A,服务A调用服务B,要在A的代码中,获取调用B的response并记录。

ClientHttpRequestInterceptor

https://www.jianshu.com/p/4d2aa3d9e8ce

拦截器实现ClientHttpRequestInterceptor,然后让restTemplate注入该拦截器,实现获取response。可以实现目标。

问题:拦截器可以在lib里面实现,集成lib的服务需要在代码中为restTemplate注入该拦截器。有一定的侵入性。如下,

public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
}


restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());

 https://www.cnblogs.com/f-anything/p/10084215.html

https://www.cnblogs.com/coderjinjian/p/9644923.html

springboot使用RestTemplate+httpclient连接池发送http消息


切面+自定义注解

https://blog.csdn.net/yanchangyufan/article/details/82821029

https://www.cnblogs.com/keeya/p/9952700.html

或者也可以服务启动时,通过切面+自定义注解,通过反射,实现获取服务A中的restTemplate类,然后注入拦截器ClientHttpRequestInterceptor

或者直接把注解打在bean上面。

@myannotion

@Autowired

Private RestTemplate restTemplate;

 

或者打在bean的定义上面

@myannotion

@Bean

public RestTemplate restTemplate() {}

 

切面+自定义注解+拦截器

 

判断拦截类的类型是否是支持的类型,如果是,比如RestTemplate或者HttpClient,则自动注入自定义的拦截器ClientHttpRequestInterceptor,在拦截器中实现业务逻辑(比如调用接口存储数据等)。

注:需要考虑,RestTemplate或者HttpClient可能是@Bean,可能是@Autowired的位置,如何方便地实现拦截,对代码的零侵入?

 

https://www.jb51.net/article/138424.htm

切片方式实现拦截

可以在配置文件中声明需要拦截的类和位置,比如com.txw.httputil.restTemplate,实现拦截。

问题:对拦截的类和方法的实现有一定的格式要求

 

拦截restTemplate请求,拦截httpClient请求

 

 

MethodInterceptor拦截器

https://www.cnblogs.com/onlymate/p/9563443.html

拦截的目标是方法,包括controller的方法。

实现MethodInterceptor拦截器有2种方式,一种是实现MethodInterceptor接口,二是利用注解或配置。

 

配置类DefaultPointcutAdvisor+拦截器类MethodInterceptor

https://blog.csdn.net/u013905744/article/details/91364736

作用到需要被拦截的类,会拦截这个类的所有方法或者某个方法。

适用于自定义的请求类,比如HttpClientUtils。

Aspect+interceptor

 

配置类DefaultPointcutAdvisor+拦截器类MethodInterceptor+自定义注解

可以直接注解到方法(ElementType.METHOD),声明只拦截该方法。

适用于自定义的请求类,该类实现了多个方法,只有部分方法需要被拦截。

@Aspect+自定义注解

更为简洁,定义一个注解,加在需要拦截的方法上,定义一个@Aspect切面类,就可以拦截指定方法的运行了。

 

更多例子

https://www.cnblogs.com/hsq666666/p/11002442.html

https://blog.csdn.net/qq_40634730/article/details/100558928

 

自定义注解+ ClientHttpRequestInterceptor

https://www.cnblogs.com/f-anything/p/10084215.html

直接注解到@Bean(ElementType.METHOD)或者@Autowired的位置(ElementType.FIELD),如果是RestTemplate或者HttpClient等支持的类,则为这个bean注册自定义的拦截器。

适用于RestTemplate或者HttpClient。

比前者的粒度更细,直接拦截了RestTemplate或者HttpClient的request和response。

前者实际是拦截方法,而方法不一定返回response,或者返回的response不符合要求。这也是前者的局限所在。

而后者的局限是要求被拦截的类支持拦截器。

 

Object target = jp.getTarget(); // 获取被代理对象

Method method = jp.getSignature(); // 获取被代理方法

Object[] args = jp.getArgs(); // 获取方法参数,通常Arrays.toString(jp.getArgs())转化后用来输出参数表列

Object result = jp.proceed(); // 调用目标方法,获取目标方法返回值--------注意抛出异常,用来定义异常是的输出。

 

  @Target(ElementType.TYPE) //接口、类、枚举、注解

  @Target(ElementType.FIELD) //字段、枚举的常量

  @Target(ElementType.METHOD) //方法

  @Target(ElementType.PARAMETER) //方法参数

  @Target(ElementType.CONSTRUCTOR) //构造函数

  @Target(ElementType.LOCAL_VARIABLE)//局部变量

  @Target(ElementType.ANNOTATION_TYPE)//注解

  @Target(ElementType.PACKAGE) ///包

 

https://yq.aliyun.com/articles/626131?spm=a2c4e.11153940.0.0.d1a57419sc3TfK

SpringBoot 项目中实现拦截功能的五种常用姿势

Webmvcconfigurer接口

https://blog.csdn.net/qq_38439885/article/details/80238813

属于配置类,配合HandlerInterceptor,或者是HandlerMethodArgumentResolver,在controller层拦截。

HandlerMethodArgumentResolver可以拦截到request,response,方法,参数。

 

BeanPostProcessor

使用BeanPostProcessor拦截所有Bean的实例化过程,

原来的做法:当每个bean实例化的时候,获取其所有的Field。如果Field的类型为支持的、需要被拦截的类型,比如RestTemplate时,就注入自定义的拦截器。

问题:

只适用于类的成员变量是RestTemplate,且已经使用@Autowired完成实例化,且加上了自定义注解,比如@Usagestat。

当RestTemplate只是作为局部变量时,拦截不到。

优化:

当每个bean实例化的时候,直接判断bean的类型。如果为支持的、需要被拦截的类型,比如RestTemplate时,就注入自定义的拦截器。

在该bean上需要加上自定义的注解。表示该bean需要被拦截。

注意:是否会重复拦截。

https://www.jianshu.com/p/369a54201943

注意:不会拦截局部变量。

 

方案

1、  拦截bean的实例化。BeanPostProcessor + ClientHttpRequestInterceptor

在bean上加注解@Autostate。

当该bean实例化的时候,直接判断bean的类型。如果为支持的、需要被拦截的类型,比如RestTemplate时,就注入自定义的拦截器(实现ClientHttpRequestInterceptor接口)。

通过自定义的拦截器获取RestTemplate每次调用的请求参数和返回值。

 

注:如果拦截restTemplate的返回,可能会造成流关闭。

需要使用restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(clientHttpRequestFactory()));

https://blog.csdn.net/yanchangyufan/article/details/82821029

 

 

2、拦截方法。@Aspect + 自定义注解

在需要被拦截的方法上加@ Autostate

自动获取该方法的所有请求参数和返回值。

或者在方法的参数上增加注解@Request,则获取该参数的值。

 

注:如果只在方法的入参增加注解,而方法上没有注解,则无法拦截。

 

3、拦截类。Aspect

在需要被拦截的类上加@ Autostate。

拦截该类的所有方法,获取方法的所有请求参数,返回值。

这种方式作为3的补充/增强。

暂不实现。

 

4、  拦截局部变量的场景?

@Target(ElementType.LOCAL_VARIABLE)

暂不支持。

 

 

Attachments: