升级至JSF2.3后使用<f:websocket />出现CdiUtils空指针异常求助
嘿,看你的异常栈就能定位到问题核心了——空指针出现在WebsocketRenderer.encodeEnd调用链里的CdiUtils.getBeanReferenceByType,这说明JSF在渲染<f:websocket>组件时,根本拿不到CDI容器提供的Bean引用。结合你用的Tomcat 9 + Spring 5 + JSF 2.3组合,问题根源很明确:Tomcat本身不带完整的CDI容器,而JSF 2.3的WebSocket功能是强制依赖CDI的,再加上Spring和JSF的DI体系需要额外适配,才导致了这个报错。
下面是一步步的解决办法:
第一步:给Tomcat补上CDI实现
Tomcat默认只提供CDI的API接口,没有具体实现。你得给项目加一个CDI容器,比如Weld或者OpenWebBeans。以Weld为例,在你的pom.xml里加这个依赖:<dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet-shaded</artifactId> <version>3.1.9.Final</version> </dependency>然后在WEB-INF目录下创建一个
beans.xml文件(内容可以很简单,主要是告诉CDI容器要扫描哪些Bean):<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all"> </beans>第二步:让JSF和Spring的DI体系打通
Spring有自己的依赖注入机制,JSF默认用CDI,得让它们俩能互相识别。你可以在faces-config.xml里配置Spring的EL解析器,让JSF用Spring的DI来获取Bean:<application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application>另外,确保你的Spring配置类里开启了组件扫描,比如加了
@ComponentScan注解,相关的WebSocket后端Bean能被Spring正确注册。第三步:检查<f:websocket>的配置是否合规
确认你在index.xhtml里的<f:websocket>标签配置没问题,比如channel属性要和后端@Push注解的通道名对应。举个例子:
前端xhtml代码:<f:websocket channel="notificationChannel" onmessage="handleNotification" />后端对应的Bean要用上CDI的
@Named注解(或者同时加Spring的@Component,让两边都能识别):@Named @RequestScoped public class NotificationPushBean { @Push(channel = "notificationChannel") private PushContext pushContext; public void sendNotification(String message) { pushContext.send(message); } }这里要注意,JSF的
@Push注解依赖CDI,所以后端Bean必须被CDI容器管理,用@Named是最直接的方式。第四步:排查Tomcat和Spring Security的干扰
首先确认Tomcat 9的WebSocket功能是正常开启的(默认是开的),项目里不要加冲突的WebSocket依赖。然后检查Spring Security的过滤器链,确保WebSocket的请求路径(默认是/javax.faces.push/*)没有被拦截,你可以在Spring Security配置里放行这个路径:@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/javax.faces.push/**").permitAll() // 其他你的配置... }
如果试了上面的步骤还是不行,还有两个小技巧:
- 把JSF版本升级到2.3.x的最新稳定版(比如2.3.14),官方在后续版本里修复了不少CDI集成的小bug
- 检查项目里有没有其他老旧的JSF或者CDI相关依赖,比如残留的JSF 2.2的jar包,这些可能会和2.3版本冲突
内容的提问来源于stack exchange,提问作者snakedog

