Spring Boot 2.7.1集成JSF2.2、PrimeFaces6.2.9时@ManagedBean未初始化问题
Mojarra 2.2.x 原生注解扫描依赖Servlet 3.0规范的ServletContainerInitializer(即JSF内置的FacesInitializer)在容器启动阶段扫描类路径下的@ManagedBean等JSF专属注解。Spring Boot 2.7 无论是使用内嵌Servlet容器还是打WAR包部署到外部容器,默认都会过滤第三方Jar包自带的ServletContainerInitializer自动注册逻辑,同时类路径扫描的过滤规则不会将业务类的注解元数据透传给Mojarra的ConfigManager,最终导致getAnnotatedClasses返回null,JSF完全无法识别标注了@ManagedBean的托管类。
以下方案按改动量从小到大排序,可根据项目实际情况选择:
方案1:显式指定扫描参数,零代码修改兼容原有JSF逻辑
不需要改动任何原有@ManagedBean标注的业务代码,仅调整初始化配置即可:- 在注册JSF组件的
ServletContextInitializer中追加核心初始化参数,强制Mojarra加载配置并扫描指定包路径:
@Bean public ServletContextInitializer jsfContextInit() { return servletContext -> { // 保留原有JSF、PrimeFaces初始化参数,追加以下配置 servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", "true"); // 替换为你项目中JSF托管Bean实际存放的包路径,多包用逗号分隔 servletContext.setInitParameter("javax.faces.annotation.PACKAGES", "com.xxx.biz.view,com.xxx.biz.jsf"); servletContext.setInitParameter("com.sun.faces.validateXml", "false"); // 不要依赖Spring自动注册Listener/Servlet,手动在ServletContext上注册保证执行顺序 servletContext.addListener(YourCustomSpringELResolverListener.class); ServletRegistration.Dynamic facesServlet = servletContext.addServlet("FacesServlet", FacesServlet.class); facesServlet.addMapping("*.xhtml"); facesServlet.setLoadOnStartup(1); }; }- 如果是打WAR包部署到外部容器,确保启动类继承
SpringBootServletInitializer时,将JSF相关配置类加入启动源,避免Spring Boot类加载器过滤掉Mojarra需要的类元数据。 - 不要直接把
FacesInitializer声明为Spring Bean注册,Spring Bean生命周期和Servlet容器启动顺序不一致,会导致Mojarra初始化时拿不到完整上下文。
- 在注册JSF组件的
方案2:将JSF托管Bean交给Spring容器管理
适合后续要长期迭代、逐步去JSF原生配置的项目:- 给所有原有
@ManagedBean类追加Spring的@Component注解,将原@ManagedProperty注入替换为@Autowired/@Inject,JSF原生作用域替换为Spring对应Web作用域(ViewScope需自行实现或引入PrimeFaces适配Spring的ViewScope实现)。 - 该方案下完全跳过Mojarra的托管Bean扫描逻辑,所有Bean由Spring容器初始化,
@PostConstruct方法由Spring在Bean创建阶段自动调用,不会出现扫描失效问题,加@Component后初始化方法正常执行已经验证了该方案的可行性。
- 给所有原有
方案3:升级Mojarra版本适配Spring Boot运行环境
Mojarra 2.2.14版本发布时间较早,对Spring Boot内嵌Servlet容器场景没有做兼容适配,如果允许升级依赖,可将Mojarra升级到2.3.x稳定版,该版本对Spring Boot场景做了官方适配,不需要额外配置扫描参数即可正常识别@ManagedBean注解,仅需要注意2.3.x部分新注解的包路径做了调整,原有@ManagedBean相关逻辑完全兼容。
配置完成后重启应用,可在自定义JSF监听器的contextInitialized方法执行完成后断点查看ConfigManager.getInstance().getAnnotatedClasses(servletContext)的返回值,若返回的Map中包含所有你定义的@ManagedBean类,即说明扫描正常,此时访问页面EL表达式不会再抛出空相关异常,@PostConstruct方法会在对应Bean首次被请求时正常执行。
常见踩坑点:如果配置了RewriteFilter,需要确保其过滤顺序在FacesServlet之后,避免请求被提前拦截导致JSF生命周期未正常触发。
内容的提问来源于stack exchange,提问作者Jacob

