You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot+JSF/CDI中@ViewScoped Bean意外表现为会话/应用作用域

问题分析与解决方案

你遇到的核心问题是标注了@Named和@ViewScoped的Bean未按预期销毁,页面切换后输入值依然保留,本质是ViewScoped作用域未被正确识别和管理,以下是具体排查和解决步骤:

1. 修正@ViewScoped注解的导入路径

你使用的是CDI的@Named注解,必须搭配JSF 2.3+兼容CDI的@ViewScoped,不能用旧版JSF原生的javax.faces.bean.ViewScoped(这个注解仅适配JSF原生的@ManagedBean,和CDI体系不兼容)。

正确的导入路径:

  • Jakarta EE 8+环境:jakarta.faces.view.ViewScoped
  • Java EE 8环境:javax.faces.view.ViewScoped

同时注意类名符合Java规范(首字母大写),并确保属性的Getter/Setter完整实现(EL表达式依赖这些方法读写值):

import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import java.io.Serializable;

@Named("parameterBean")
@ViewScoped
public class ParameterBean implements Serializable {
    private String spreadValue;

    // 必须实现Getter和Setter
    public String getSpreadValue() {
        return spreadValue;
    }

    public void setSpreadValue(String spreadValue) {
        this.spreadValue = spreadValue;
    }
}

2. 给Spring Boot添加ViewScoped支持

Spring Boot默认不原生支持JSF的ViewScoped,需要手动配置作用域管理器,让Spring容器能正确管理ViewScoped Bean的生命周期。在你的JSFConfig类中补充以下代码:

import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.faces.context.FacesContext;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.annotation.WebServlet;
import javax.faces.webapp.FacesServlet;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class JSFConfig {

    @Bean
    ServletRegistrationBean<FacesServlet> jsfServletRegistration(ServletContext servletContext) {
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
        ServletRegistrationBean<FacesServlet> srb = new ServletRegistrationBean<>();
        srb.setServlet(new FacesServlet());
        srb.setUrlMappings(Arrays.asList("*.xhtml"));
        srb.setLoadOnStartup(1);
        return srb;
    }

    // 添加ViewScope的Spring配置
    @Bean
    public CustomScopeConfigurer viewScopeConfigurer() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();
        Map<String, Object> scopes = new HashMap<>();
        scopes.put("view", new ViewScope());
        configurer.setScopes(scopes);
        return configurer;
    }

    // 自定义ViewScope实现,绑定JSF视图上下文
    static class ViewScope implements org.springframework.beans.factory.config.Scope {
        @Override
        public Object get(String name, org.springframework.beans.factory.ObjectFactory<?> objectFactory) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            if (facesContext == null) {
                throw new IllegalStateException("FacesContext未初始化");
            }
            Map<String, Object> viewMap = facesContext.getViewRoot().getViewMap();
            if (!viewMap.containsKey(name)) {
                viewMap.put(name, objectFactory.getObject());
            }
            return viewMap.get(name);
        }

        @Override
        public Object remove(String name) {
            return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
        }

        @Override
        public void registerDestructionCallback(String name, Runnable callback) {}

        @Override
        public Object resolveContextualObject(String key) { return null; }

        @Override
        public String getConversationId() { return null; }
    }
}

3. 确保页面导航使用重定向

如果页面切换用的是服务器转发(forward),会复用同一个视图上下文,ViewScoped Bean不会销毁。必须使用重定向来触发视图上下文的切换:

  • 导航规则中添加<redirect/>:
<!-- faces-config.xml -->
<navigation-rule>
    <from-view-id>/page1.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>toPage2</from-outcome>
        <to-view-id>/page2.xhtml</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>
  • 或者在Bean的导航方法中返回带?faces-redirect=true的结果:
public String switchPage() {
    return "/targetPage.xhtml?faces-redirect=true";
}

4. 优化冗余的AJAX配置

你的p:inputText同时配置了change和valueChange两个AJAX事件,PrimeFaces中这两个事件的行为几乎完全重复,保留一个即可,避免不必要的请求:

<p:inputText value="#{parameterBean.spreadValue}" label=" "
             styleClass="inputText" id="spreadValue">
    <p:ajax event="change" />
</p:inputText>

内容的提问来源于stack exchange,提问作者SmokingTurtle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:41:05