JSF中RequestScoped Bean扩展PartialViewContext构造器冲突求解
问题根因
当前实现的核心矛盾是把两类生命周期完全不同的对象职责耦合在了同一个类中:
- JSF的
PartialViewContext是通过JSF工厂链创建的对象,新版API强制要求Wrapper实现必须通过构造器接收被包装的实例,本身不依赖CDI托管 - 带
@Observes监听的请求域状态对象必须由CDI容器托管,才能正常接收事件、在请求内共享状态
之前直接手动new出来的非CDI托管类实例不在CDI容器的管理范围内,CDI不会为这个实例注册事件监听器,自然无法收到事件。
可行实现方案
通过职责拆分完全避开两个规范的冲突,不需要使用任何废弃API,也能完整保留原有功能:
1. 拆分独立的CDI托管请求域状态/监听Bean
单独创建一个不继承任何JSF API类的纯CDI Bean,负责存储请求内共享状态、处理CDI事件监听,这个类完全遵循CDI规范,保留无参构造器即可,不存在JSF API兼容问题:
@RequestScoped public class OurPartialViewState { private final List<OurRefreshEvent> pendingEvents = new ArrayList<>(); // 事件监听方法放在纯CDI Bean里,容器会自动识别触发 void onRefreshEvent(@Observes OurRefreshEvent event) { pendingEvents.add(event); // 原有事件处理的所有逻辑都迁移到这里 } public List<OurRefreshEvent> getPendingEvents() { return pendingEvents; } // 其他需要在请求内共享给PartialResponseWriter的属性,也全部定义在这个类里 }
2. 改造自定义PartialViewContext实现
让OurPartialViewContext严格遵循新版JSF Wrapper规范,只保留接收被包装对象的构造器,移除所有CDI作用域注解,不再作为CDI Bean直接托管。需要使用请求域状态时,通过CDI API延迟获取刚才定义的请求域Bean即可:
public class OurPartialViewContext extends PartialViewContextWrapper { private final PartialViewContext wrapped; private OurPartialViewState state; private PartialResponseWriter writer; // 完全符合新版API要求,仅保留带被包装实例的构造器,不使用废弃的无参构造器 public OurPartialViewContext(PartialViewContext wrapped) { this.wrapped = wrapped; } @Override public PartialViewContext getWrapped() { return wrapped; } // 延迟获取CDI托管的请求域状态,单次请求内只会查找一次,无性能问题 private OurPartialViewState getState() { if (state == null) { state = CDI.current().select(OurPartialViewState.class).get(); } return state; } @Override public PartialResponseWriter getPartialResponseWriter() { if (writer == null) { // 把请求域状态传给自定义的ResponseWriter使用 writer = new OurPartialResponseWriter(super.getPartialResponseWriter(), getState()); } return writer; } // 其他原有方法的实现,需要访问事件和状态时统一从getState()获取即可 }
3. 改造PartialViewContextFactory
工厂类不再注入自定义的PartialViewContext实例,遵循JSF工厂规范,在获取上下文时手动包装原始实例即可:
public class OurPartialViewContextFactory extends PartialViewContextFactory { // JSF工厂要求的标准构造器,接收被包装的工厂实例 public OurPartialViewContextFactory(PartialViewContextFactory wrapped) { super(wrapped); } @Override public PartialViewContext getPartialViewContext(FacesContext context) { PartialViewContext originalContext = getWrapped().getPartialViewContext(context); // 传入原始实例创建自定义Wrapper,完全符合API要求 return new OurPartialViewContext(originalContext); } }
方案说明
- 所有JSF相关的Wrapper、Factory实现完全遵循新版API规范,没有调用任何废弃方法,不会产生兼容警告
- 事件监听、请求状态存储逻辑完全由CDI容器托管,
@Observes方法可以正常被触发,状态在单次请求生命周期内全局可访问,完全满足给自定义PartialResponseWriter共享数据的需求 - 不要尝试通过
@Inject标注带参构造器的方式直接把PartialViewContext做成CDI Bean:PartialViewContext本身是JSF工厂链创建的对象,不属于CDI托管的Bean,构造器注入会触发找不到依赖的错误。
内容的提问来源于stack exchange,提问作者Xavier Portebois
相关产品推荐
相关产品推荐

