@RefreshScope和过滤器Filter不要这样用

  |   0 评论   |   0 浏览

1、问题

最近在一个Spring应用中碰到这样的问题:Spring过滤器OncePerRequestFilterdoFilterInternal方法一直不被执行。最终发现是因为:在注册自定义的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之所有能做热加载,主要做了以下动作:

  1. 单独管理Bean生命周期
    创建Bean的时候如果是RefreshScope就缓存在一个专门管理的ScopeMap中,这样就可以管理Scope是Refresh的Bean的生命周期了。
  2. 重新创建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不要这样一起使用,否则会导致OncePerRequestFilterdoFilterInternal方法一直不被执行。

如果有知道谜底的朋友也请帮我解答下,谢谢!!!

文中查阅的RefreshScope的原理,参考了这篇CSDN博主Static_lin的文章《从RefreshScope实现原理看刷新配置失效问题》,文章用源码将原理分析的比较透彻。

总结 :本文简单分析@RefreshScope和过滤器Filter一起使用出现的问题,最终谜底仍未解开,只能给朋友们提个醒了。

本篇完结!感谢你的阅读,欢迎点赞 关注 收藏 私信!!!

原文链接: http://www.mangod.top/articles/2023/08/27/1693094046480.htmlhttps://mp.weixin.qq.com/s/23eZm-mdtj7J_Pg9TUUKNA


标题:@RefreshScope和过滤器Filter不要这样用
作者:程序员半支烟
地址:http://mangod.top/articles/2023/09/02/1693623653306.html