- 过滤器(Filter)
- HandlerInterceptor
- 拦截器Interceptor和过滤器Filter的区别
- 过滤器Filter、拦截器Interceptor和切片AOP的区别和调用顺序
- 需求
- ClientHttpRequestInterceptor
- 切面+自定义注解
- 切片方式实现拦截
- MethodInterceptor拦截器
- 配置类DefaultPointcutAdvisor+拦截器类MethodInterceptor
- 配置类DefaultPointcutAdvisor+拦截器类MethodInterceptor+自定义注解
- @Aspect+自定义注解
- 自定义注解+ ClientHttpRequestInterceptor
- Webmvcconfigurer接口
- BeanPostProcessor
- 方案
拦截器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)
暂不支持。

