重写@PostConstruct在CDI视图范围的bean上调用两次(Weld,Seam 3)

时间:2013-07-16 16:46:08

标签: java jsf-2 cdi seam3 weld

我们遇到基类层次结构上的重复@PostConstruct调用问题。

首先是基类:

public abstract class AbstractManager<T> implements Serializable
{
    private List<T> entities;

    @PostConstruct // when annotated with @PostConstruct this method is called even if overridden in sub class
    public void init()
    {
        System.out.println( AbstractManager.class.getSimpleName() + " @PostConstruct on " + this.getClass().getSimpleName() + "!" );
    }

    protected abstract List<T> getDbEntities();

    public List<T> getEntities()
    {
        if ( this.entities == null )
        {
            this.entities = this.getDbEntities();
        }

        return this.entities;
    }

    public void setEntities( List<T> entities )
    {
        this.entities = entities;
    }

    public void clearEntities()
    {
        this.entities = null;
    }
}

这是具体的子类(注意如何重写init()来调用super.init()):

@Named
@ViewScoped
public class PseudoEntityManager extends AbstractManager<PseudoEntity>
{
    private static final long serialVersionUID = 1L;

    @Override
    @PostConstruct
    public void init()
    {
        super.init();
    }

    ...
}

当呈现某个(未示出的)页面时,pseudoEntityManager bean被实例化,但是@PostConstruct被调用两次。这是输出:

INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: New list of pseudo DB entities!

具体子类中注释重写init()方法时,超类中只有一个@PostConstruct方法,生成以下输出:

INFO: AbstractManager @PostConstruct on PseudoEntityManager!
INFO: New list of pseudo DB entities!

现在根据CDI规范,正确的行为是什么? (引用任何人?)

说明:

我在研究时也发现了这个邮件列表对话:

http://list-archives.org/2012/10/11/cdi-dev-lists-jboss-org/postconstruct-on-inherited-class/f/4426568582

在对话中,一些大师倾向于说“只应调用子类上的@PostConstruct方法”。如果你仔细阅读,那么就有一个指向焊接错误的链接,据说自Weld 1.1.5起就解决了这个问题:

https://issues.jboss.org/browse/WELD-1225

这个真的已经修复了吗?根据我得到的输出,它不是。

环境:焊接1.1.8以及Seam 3以使CDI @ViewScoped正常工作(在GlassFish 3.1.2上)。

2 个答案:

答案 0 :(得分:1)

是的,它已被修复。不幸的是,它仅在Weld 2.0版本系列中得到修复。有时这些错误会被移植回来,但遗憾的是我怀疑这个错误最终会在维护版本中得到解决。

答案 1 :(得分:1)

这是一个解决方法的答案。

提到Bug时,会考虑@PostConstruct注释,并执行它们的逻辑代码。

  1. 使用@Override将执行超类的帖子,该子类由子类
  2. 覆盖
  3. 然后也会执行子类上的帖子。
  4. 要解决此问题,可以执行以下操作:

    • 定义新签名并将其标记为@PostConstruct
    • 覆盖标记为@PostConstruct的超类方法,但不要将@PostConstruct Annotation,只是@Override,并将其实现为空。

    结果你只会在你的子类上执行标记为@PostConstruct的代码:

    @Specializes
    @ViewAccessScoped
    public class BaseBean extends SubBean {
    
    @PostConstruct
    public void postConstructExtension() {
        LOGGER.info("POST CALLED ON SPECIALIZED CLASS" + this.getClass().getSimpleName());
    }
    
    @Override
    public void postConstruct() {
        LOGGER.info("OVERRIDDEN POST CALLED ON SPECIALIZED CLASS" + this.getClass().getSimpleName());
    }
    

    }