传统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
-
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); } }
-
<!-- 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; }