gpt4 book ai didi

jsf-2 - ViewMapListener JSF 未被调用

转载 作者:行者123 更新时间:2023-12-01 05:27:03 29 4
gpt4 key购买 nike

我正在尝试移植 JSF @ViewScoped CDI 注释。原因是更多的教育而不是基于需要。我选择这个特定的范围主要是因为缺少一个可能希望在 CDI 中实现的自定义范围的更好的具体示例。

也就是说,我的出发点是Porting the @ViewScoped JSF annotation to CDI .但是,这个实现并没有考虑到 Context 一个看似非常重要的职责。 API 中提到的(即破坏) :

The context object is responsible for creating and destroying contextual instances by calling operations of Contextual. In particular, the context object is responsible for destroying any contextual instance it creates by passing the instance to Contextual.destroy(Object, CreationalContext). A destroyed instance must not subsequently be returned by get(). The context object must pass the same instance of CreationalContext to Contextual.destroy() that it passed to Contextual.create() when it created the instance.



我决定通过我的 Context 添加此功能。目的:
  • 跟踪什么Contextual它为其创建的对象UIViewRoot小号;
  • 实现 ViewMapListener接口(interface)并将自己注册为每个 UIViewRoot 的监听器调用 UIViewRoot.subscribeToViewEvent(PreDestroyViewMapEvent.class, this) ;
  • 销毁任何创建的Contextual s 当ViewMapListener.processEvent(SystemEvent event)被调用并从该 UIViewRoot 注销自身.

  • 这是我的 Context执行:
    package com.example;

    import java.lang.annotation.Annotation;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    import java.util.logging.Level;
    import javax.enterprise.context.spi.Context;
    import javax.enterprise.context.spi.Contextual;
    import javax.enterprise.context.spi.CreationalContext;
    import javax.enterprise.inject.spi.Bean;
    import javax.faces.component.UIViewRoot;
    import javax.faces.context.FacesContext;
    import javax.faces.event.AbortProcessingException;
    import javax.faces.event.PreDestroyViewMapEvent;
    import javax.faces.event.SystemEvent;
    import javax.faces.event.ViewMapListener;

    public class ViewContext implements Context, ViewMapListener {

    private Map<UIViewRoot, Set<Disposable>> state;

    public ViewContext() {
    this.state = new HashMap<UIViewRoot, Set<Disposable>>();
    }

    // mimics a multimap put()
    private void put(UIViewRoot key, Disposable value) {
    if (this.state.containsKey(key)) {
    this.state.get(key).add(value);
    } else {
    HashSet<Disposable> valueSet = new HashSet<Disposable>(1);
    valueSet.add(value);
    this.state.put(key, valueSet);
    }
    }

    @Override
    public Class<? extends Annotation> getScope() {
    return ViewScoped.class;
    }

    @Override
    public <T> T get(final Contextual<T> contextual,
    final CreationalContext<T> creationalContext) {
    if (contextual instanceof Bean) {
    Bean bean = (Bean) contextual;
    String name = bean.getName();
    FacesContext ctx = FacesContext.getCurrentInstance();
    UIViewRoot viewRoot = ctx.getViewRoot();
    Map<String, Object> viewMap = viewRoot.getViewMap();
    if (viewMap.containsKey(name)) {
    return (T) viewMap.get(name);
    } else {
    final T instance = contextual.create(creationalContext);
    viewMap.put(name, instance);
    // register for events
    viewRoot.subscribeToViewEvent(
    PreDestroyViewMapEvent.class, this);
    // allows us to properly couple the right contaxtual, instance, and creational context
    this.put(viewRoot, new Disposable() {

    @Override
    public void dispose() {
    contextual.destroy(instance, creationalContext);
    }

    });
    return instance;
    }
    } else {
    return null;
    }
    }

    @Override
    public <T> T get(Contextual<T> contextual) {
    if (contextual instanceof Bean) {
    Bean bean = (Bean) contextual;
    String name = bean.getName();
    FacesContext ctx = FacesContext.getCurrentInstance();
    UIViewRoot viewRoot = ctx.getViewRoot();
    Map<String, Object> viewMap = viewRoot.getViewMap();
    if (viewMap.containsKey(name)) {
    return (T) viewMap.get(name);
    } else {
    return null;
    }
    } else {
    return null;
    }
    }

    // this scope is only active when a FacesContext with a UIViewRoot exists
    @Override
    public boolean isActive() {
    FacesContext ctx = FacesContext.getCurrentInstance();
    if (ctx == null) {
    return false;
    } else {
    UIViewRoot viewRoot = ctx.getViewRoot();
    return viewRoot != null;
    }
    }

    // dispose all of the beans associated with the UIViewRoot that fired this event
    @Override
    public void processEvent(SystemEvent event)
    throws AbortProcessingException {
    if (event instanceof PreDestroyViewMapEvent) {
    UIViewRoot viewRoot = (UIViewRoot) event.getSource();
    if (this.state.containsKey(viewRoot)) {
    Set<Disposable> valueSet = this.state.remove(viewRoot);
    for (Disposable disposable : valueSet) {
    disposable.dispose();
    }
    viewRoot.unsubscribeFromViewEvent(
    PreDestroyViewMapEvent.class, this);
    }
    }
    }

    @Override
    public boolean isListenerForSource(Object source) {
    return source instanceof UIViewRoot;
    }

    }

    这是 Disposable界面:
    package com.example;

    public interface Disposable {

    public void dispose();

    }

    这是范围注释:
    package com.example;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import javax.enterprise.context.NormalScope;

    @Inherited
    @NormalScope
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD,
    ElementType.FIELD, ElementType.PARAMETER})
    public @interface ViewScoped {

    }

    这是 CDI 扩展声明:
    package com.example;

    import javax.enterprise.event.Observes;
    import javax.enterprise.inject.spi.AfterBeanDiscovery;
    import javax.enterprise.inject.spi.Extension;

    public class CustomContextsExtension implements Extension {

    public void afterBeanDiscovery(@Observes AfterBeanDiscovery event) {
    event.addContext(new ViewContext());
    }

    }

    我添加了 javax.enterprise.inject.spi.Extension META-INF/services 下的文件包含 com.example.CustomContextsExtension向 CDI 正确注册上述内容。

    我现在可以制作像这样的 bean(注意使用自定义 @ViewScoped 实现。):
    package com.example;

    import com.concensus.athena.framework.cdi.extension.ViewScoped;
    import java.io.Serializable;
    import javax.inject.Named;

    @Named
    @ViewScoped
    public class User implements Serializable {
    ...
    }

    bean 被正确地创建并正确地注入(inject)到 JSF 页面中(即每个 View 返回相同的实例,仅在创建 View 时创建新的实例,相同的实例通过多个请求注入(inject)到同一个 View )。我怎么知道?想象一下上面的代码中到处都是调试代码,为了清楚起见,我特意删除了这些代码,因为这已经是一篇很大的文章了。

    问题是我的 ViewContext.isListenerForSource(Object source)ViewContext.processEvent(SystemEvent event)永远不会被调用。我期望至少在 session 到期时会调用这些事件,因为 View 映射存储在 session 映射中(对吗?)。我将 session 超时设置为 1 分钟,等待,看到超时发生,但仍然没有调用我的监听器。

    我还尝试将以下内容添加到我的 faces-config.xml (主要是因为缺乏想法):
    <system-event-listener>
    <system-event-listener-class>com.example.ViewContext</system-event-listener-class>
    <system-event-class>javax.faces.event.PreDestroyViewMapEvent</system-event-class>
    <source-class>javax.faces.component.UIViewRoot</source-class>
    </system-event-listener>

    最后,我的环境是 JBoss AS 7.1.1Mojarra 2.1.7 .

    任何线索将不胜感激。

    编辑:进一步调查。

    PreDestroyViewMapEvent PostConstructViewMapEvent 时似乎根本没有被解雇按预期触发 - 每次创建新 View map 时,特别是在 UIViewRoot.getViewMap(true) 期间.文档指出 PreDestroyViewMapEvent每次都应该被解雇 clear()在 View map 上调用。令人怀疑的是-是 clear()需要调用吗?如果有,什么时候?

    我能够在文档中找到此类要求的唯一位置是 FacesContext.setViewRoot() :

    If the current UIViewRoot is non-null, and calling equals() on the argument root, passing the current UIViewRoot returns false, the clear method must be called on the Map returned from UIViewRoot#getViewMap.



    这在正常的 JSF 生命周期中是否发生过,即没有以编程方式调用 UIViewRoot.setViewMap() ?我似乎找不到任何迹象。

    最佳答案

    这与 JSF2.2 规范中正在修复的 JSF 规范的问题有关,请参阅 here .另外,我用 Apache DeltaSpike 创建了一个问题,所以他们可能会尝试修复它,请参阅 here .如果它在 DeltaSpike 中得到修复,那么它最终可能也会被合并到 CODI 和/或 Seam 中。

    关于jsf-2 - ViewMapListener JSF 未被调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13374211/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com