传统spring-mvc中的filter进行自动注入

传统spring踩坑

filter 中的自动注入

最近把一个用于springboot中的组件,要放入传统的spring-mvc中去,以为没什么问题。直到拉了一个前置机工程(spring-mvc),中间的一个过滤器都是空指针。

大概代码如下

@Component
@WebFilter(urlPatterns = "/*", filterName = "rateAndMetricsFilter")
public class RateAndMetricsFilter implements Filter, Runnable {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${enable.db.store:false}")
    private boolean enableDBStore = false;

    @Autowired
    private RateLimiter rateLimiter;
  
@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 限流了抛出运行时异常
        rateLimiter.limit(uri);
        chain.doFilter(servletRequest, servletResponse);
    }
}

中间的这个rateLimiter,自动注入未成功一直导致空指针。

传统spring-mvc的启动流程 context-param -> listener -> filter -> servlet,servlet在filter之后也就意味着org.springframework.web.servlet.DispatcherServlet都没有实例化,如此一来自动注入肯定会失败

解决方案

有2种,一种显示初始化spring容器,第二种将过滤器在xml中交给spring的delegate

  1.     public void init(FilterConfig filterConfig) throws ServletException {
            //在filter 获取spring上下文
            ServletContext context = filterConfig.getServletContext();
            ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
            if(enableDBStore){
                jdbcTemplate = ctx.getBean(JdbcTemplate.class);
            }
            rateLimiter = ctx.getBean(RateLimiter.class);
            Environment environment = ctx.getEnvironment();
            if(environment.containsProperty("enable.db.store")){
                enableDBStore = environment.getProperty("enable.db.store",Boolean.class);
            }
        }
    
  2. <!-- web.xml中添加DelegatingFilterProxy, -->
    <filter>
    		<filter-name>frontMonitorFilter</filter-name>
    		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    		<init-param>
    			<param-name>targetBeanName</param-name>
    			<param-value>rateAndMetricsFilter</param-value>
    		</init-param>
    	</filter>
    <!-- 声明这个bean -->
    	<bean id="rateAndMetricsFilter" class="com.gzhc365.limit.filter.RateAndMetricsFilter"></bean>
    
    

    仔细查看了一下DelegatingFilterProxy的源代码,他也是获取springContext,在到springContext中获取这个filter

        protected void initFilterBean() throws ServletException {
            synchronized(this.delegateMonitor) {
                if (this.delegate == null) {
                    if (this.targetBeanName == null) {
                        this.targetBeanName = this.getFilterName();
                    }
    
                    WebApplicationContext wac = this.findWebApplicationContext();
                    if (wac != null) {
                        this.delegate = this.initDelegate(wac);
                    }
                }
    
            }
        }
        protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
            Filter delegate = (Filter)wac.getBean(this.getTargetBeanName(), Filter.class);
            if (this.isTargetFilterLifecycle()) {
                delegate.init(this.getFilterConfig());
            }
    
            return delegate;
        }