1、问题
最近在一个Spring应用中碰到这样的问题:Spring过滤器OncePerRequestFilter
的doFilterInternal
方法一直不被执行。最终发现是因为:在注册自定义的OncePerRequestFilter
所在的类上加了@RefreshScope
导致自定义的OncePerRequestFilter
不会被注册到上下文。
模拟的代码如下:
自定义的Filter:
@Slf4j
public class CustomFilter extends OncePerRequestFilter {
public CustomFilter() {
log.warn("=================init CustomFilter====================");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setStatus(200);
response.setHeader("Content-Type", "application/json");
response.getWriter().write("{\"age\":12}");
response.getWriter().flush();
response.flushBuffer();
// filterChain.doFilter(request, response);
}
}
使用@RefreshScope注解的类
@Configuration
@Slf4j
@RefreshScope
public class WebConfiguration implements WebMvcConfigurer {
@Bean
public CustomFilter customFilter() {
return new CustomFilter();
}
}
2、寻找原因
对于这个问题,在Google上也没搜索到合适的解答,自己在源码里打了断点,发现这个CustomFilter没有被执行init方法,所以最终也不会打印Filter xxx configured for use
的日志,也就是说不会被注册进上下文。
我们知道filter过滤器是用于过滤请求的URL,遵循Servlet规范的,需要实现javax.servlet.Filter接口,依赖于Tomcat等容器。
Filter有3个的生命周期:服务启动时-init(FilterConfig arg0)
、服务停止时-destroy()
、拦截请求时-doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain filterChain)
。
只有过滤器执行了init方法,才能被注册进上下文。那上面自定义的过滤器CustomFilter为什么没有被执行init方法呢。
继续查阅了@ScopeRefesh相关原理,我们知道RefreshScope之所有能做热加载,主要做了以下动作:
- 单独管理Bean生命周期
创建Bean的时候如果是RefreshScope就缓存在一个专门管理的ScopeMap中,这样就可以管理Scope是Refresh的Bean的生命周期了。 - 重新创建Bean
外部化配置刷新之后,会触发一个动作,这个动作将上面的ScopeMap中的Bean清空,这样,这些Bean就会重新被IOC容器创建一次,使用最新的外部化配置的值注入类中,达到热加载新值的效果。
@ScopeRefesh也有失效的场景,RefreshBean被正确加载为CGLib动态代理对象就能正常动态刷新,有些不正常加载的场景,则会失效。比如组装加载WebFilter的时候会有两个重复Filter,一个是变化的,一个是不变化的。在Web应用里实际取到的是不变化的,所以修改配置会失效。
3、未果,惨淡收场
结合现状和原理来看,我还是未能破解这个谜题。上文中的问题是加载出了2个Filter,导致失效,而我这里出现的问题是自定义的Filter没有被加载。
我的猜测是,这个自定义Filter可能也是被创建了2个Bean,Bean-A是走了init方法,但实际应用中取到的Bean-B,由于某些原因,Bean-B再次创建的时候,没有走init方法,所以导致没有被注册到上下文。
或者是只创建一个Bean,但是由于使用@RefreshScope创建的Filter Bean缺少什么机制,导致没有被当作一种Filter去执行初始化。
折腾了几个小时,最终也没分析出个原因。这里只能给朋友们提个醒,@RefreshScope和过滤器Filter不要这样一起使用,否则会导致OncePerRequestFilter
的doFilterInternal
方法一直不被执行。
如果有知道谜底的朋友也请帮我解答下,谢谢!!!
文中查阅的RefreshScope的原理,参考了这篇CSDN博主Static_lin的文章《从RefreshScope实现原理看刷新配置失效问题》,文章用源码将原理分析的比较透彻。
总结:本文简单分析@RefreshScope和过滤器Filter一起使用出现的问题,最终谜底仍未解开,只能给朋友们提个醒了。
本篇完结!感谢你的阅读,欢迎点赞 关注 收藏 私信!!!
原文链接:http://www.mangod.top/articles/2023/09/02/1693623653306.html、https://mp.weixin.qq.com/s/vZCjamIzDGgRef_m1YbXHA