BeanManager始终返回相同的引用

时间:2017-02-15 17:56:03

标签: hash scope cdi custom-scope

我正在创建自定义CDI范围,并使用BeanManager来注入我的NavigationHandler自定义类。但它返回的豆子很奇怪。

所以我这样使用BeanManager:

public class ScreenContext implements Context
{
    private NavigationHandler getNavigationHandler()
    {
        final Set<Bean<?>> beans = m_beanManager.getBeans(NavigationHandler.class);
        final Bean<?> bean = m_beanManager.resolve(beans);

        NavigationHandler reference =
            (NavigationHandler) m_beanManager.getReference(bean, NavigationHandler.class,
                m_beanManager.createCreationalContext(bean));

        System.out.println("Found "+reference+" (hash="+reference.hashCode()+")");
        return reference;
    }
    ...
}

我希望,当我使用两个不同的浏览器使用我的项目时,得到两个不同的NavigationHandler,它们是这样定义的:

@Named
@WindowScoped
public class NavigationHandler
    implements Serializable, INavigationHandlerController

但是当我测试true时,我的调试器会返回reference1==reference2。我也得到奇怪的哈希码:

Found NavigationHandler@593e785f (hash=1261587818)
Found NavigationHandler@b6d51bd (hash=1261587818)

我不明白为什么toString()中使用的哈希值不同,但hashCode()中使用的哈希值是相同的。

1 个答案:

答案 0 :(得分:1)

我想我找出了这两个相关问题的原因,这是一个棘手的问题!

m_beanManager.getReference(..)不会返回NavigationHandler实例,而是返回一个代理,它应该在范围的上下文中选择并充当正确的NavigationHandler。

了解Proxy / Context / BeanManager概念的链接: https://developer.jboss.org/blogs/stuartdouglas/2010/10/12/weld-cdi-and-proxies

所以我的getNavigationHandler()方法不适合这项工作:我调用此方法的池将包含NavigationHandler代理而不是NavigationHandlers。由于我的池不是@Inject ed字段,因此CDI不会自动更新代理,因此返回的引用始终是代理主动使用的最后一个上下文中的引用。

出于同样的原因输出:

Found NavigationHandler@593e785f (hash=1261587818)
Found NavigationHandler@b6d51bd (hash=1261587818)

在一种情况下,我得到NavigationHandler实例的哈希值,在另一种情况下,我得到NavigationHandler代理的哈希值。但我不知道哪一个是哪个。我愿意相信使用代理的toString(),因为beanManager.getReference(..)每次都应该为一个新的代理提供服务,而hashCode应该对每个实例的对象都是唯一的。

表示每个实例的哈希码都是唯一哈希码且不能随时间变化的链接: http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode%28%29

因此,实施getNavigationHandler()的正确方法是:

private getNavigationHandlergetgetNavigationHandler()
{
    final Set<Bean<?>> beans = m_beanManager.getBeans(getNavigationHandler.class);
    final Bean<?> bean = m_beanManager.resolve(beans);

    /* Works : pure reference (not proxied) */
    Class<? extends Annotation> scopeType = bean.getScope();
    Context context = m_beanManager.getContext(scopeType);
    CreationalContext<?> creationalContext = m_beanManager.createCreationalContext(bean);
    // Casts below are necessary since inheritence does not work for templates
    getNavigationHandler reference =
        context.get((Bean<NavigationHandler>) bean, (CreationalContext<NavigationHandler>) creationalContext);

    return reference;
}

解释 beanManager.getReference(..) beanManager.getContext(..).get(..)之间差异的链接:Canonical way to obtain CDI managed bean instance: BeanManager#getReference() vs Context#get()