如何在非Spring类或new实例化类中@Autowired?Filter注入Service遇NPE
嘿,这个问题我太熟了!你遇到的NullPointerException根源很明确:你的CORSFilter是由Servlet容器(比如Tomcat)初始化和管理的对象,不属于Spring的Bean容器范畴,所以Spring根本没法帮你自动注入@Autowired标记的AccessLogService,自然调用的时候就会空指针啦。
下面给你几种实用的解决方案,按推荐程度排序:
方案一:把Filter交给Spring管理(最推荐)
这种方式最贴合Spring的设计思想,让Filter成为Spring Bean,再通过Spring提供的代理类让Servlet容器识别它。
步骤1:将Filter标记为Spring组件
给CORSFilter加上@Component注解,让Spring把它纳入Bean管理:
@Component public class CORSFilter implements Filter { @Autowired private AccessLogService accessLogService; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { AccessLogDTO rld = new AccessLogDTO(req); accessLogService.addAccessLog(rld); // 别忘了继续执行过滤器链 chain.doFilter(req, res); } // init和destroy方法可以留空实现,除非你有初始化/清理逻辑 @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }
步骤2:注册Filter到Servlet容器
如果是Spring Boot项目,直接用Java配置注册Filter即可:
@Configuration public class FilterRegistrationConfig { @Bean public FilterRegistrationBean<CORSFilter> corsFilterRegistration(CORSFilter corsFilter) { FilterRegistrationBean<CORSFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(corsFilter); // 设置你需要拦截的URL路径,比如所有请求 registrationBean.addUrlPatterns("/*"); // 可以设置过滤器顺序,按需调整 registrationBean.setOrder(1); return registrationBean; } }
如果是传统的Spring MVC项目(用web.xml),则配置DelegatingFilterProxy来代理你的Spring Filter Bean:
<filter> <filter-name>cORSFilter</filter-name> <!-- 这个是Spring提供的代理类,负责把请求转交给Spring管理的Filter --> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>cORSFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
注意:这里的filter-name必须和你的Filter Bean名称一致(默认是类名首字母小写,比如cORSFilter或者corsFilter,如果自定义了@Component的value就用自定义名称)。
方案二:在Filter的init方法中手动获取Spring Bean
如果你不想把Filter交给Spring管理,可以在Filter初始化时,手动从Spring的Web上下文里取出需要的Service:
public class CORSFilter implements Filter { private AccessLogService accessLogService; @Override public void init(FilterConfig filterConfig) throws ServletException { // 从ServletContext中获取Spring的WebApplicationContext WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext()); // 从上下文获取对应的Service Bean accessLogService = springContext.getBean(AccessLogService.class); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { AccessLogDTO rld = new AccessLogDTO(req); accessLogService.addAccessLog(rld); chain.doFilter(req, res); } @Override public void destroy() {} }
这种方法的前提是Spring的WebApplicationContext已经被初始化(一般Spring MVC项目都会通过ContextLoaderListener加载上下文,所以没问题)。
方案三:使用@Configurable(不推荐,配置复杂)
这个方法需要启用AspectJ织入,让Spring即使对new出来的对象也能注入依赖,配置起来比较繁琐,除非你有特殊场景,否则不建议用。简单示例:
- 给Filter加上
@Configurable注解:
@Configurable public class CORSFilter implements Filter { @Autowired private AccessLogService accessLogService; // 其他方法不变 }
- 然后在Spring配置中启用AspectJ自动代理,并配置织入相关的依赖和插件,这个过程比较麻烦,所以优先前两种方案。
内容的提问来源于stack exchange,提问作者Magno C

