使用 CDI/Weld 注入通用 Bean

Posted

技术标签:

【中文标题】使用 CDI/Weld 注入通用 Bean【英文标题】:Injecting generic Beans with CDI/Weld 【发布时间】:2011-09-19 22:35:49 【问题描述】:

我刚从我小小的 JavaSE/Guice 世界中走出来,目前正在探索“由容器携带”-EE6 的路径。在使用 Glassfish3.1 遇到一些问题后,我刚刚切换到 JBoss,现在遇到了一个不应该出现的问题。

作为基础设施辅助类,我正在尝试为任何类型的实体创建通用存储库/DAO。以非常简单的方式,这可能看起来像这样。

public class Repository<E, K extends Serializable & Comparable<K>> 

    private final Instance<EntityManager> entityManagerInstance;

    protected final Class<E> getDomainObjectClass() 
        return domainObjectClass;
    

    private final Class<E> domainObjectClass;

    protected final EntityManager getEntityManager() 
            return entityManagerInstance.get();
    

    @Inject
    public Repository(Instance<EntityManager> entityManageryProvider, Provider<E> domainObjectProvider) 
            //This is a dirty hack, sadly :(
            domainObjectClass = (Class<E>)domainObjectProvider.get().getClass();
            this.entityManagerInstance = entityManageryProvider;
    

    public final void persist(E domainObject) 
        final EntityManager em = getEntityManager();
        em.persist(domainObject);
    

    public final Collection<E> getAllEntities() 
            final EntityManager em = getEntityManager();
            final CriteriaBuilder cb = em.getCriteriaBuilder();
            final CriteriaQuery<E> query = cb.createQuery(getDomainObjectClass());

            final List<E> result = em.createQuery(query).getResultList();
            return Collections.unmodifiableList(result);
    

    public final E find(K id) 
            Preconditions.checkNotNull(id);
            final EntityManager em = getEntityManager();
            return em.find(getDomainObjectClass(), id);
    

    // [...]

现在可能有一个 bean 不需要依赖实体的查询功能,而只需要某种实体类型的存储库,比如(可能是一个测试用例):

public class DomainObjectARepositoryTest

    @Inject
    Repository<DomainObjectA, PersistableUUID> domainObjectARepository;


    @Test
    public void testMitarbeitererstellung() 
        for (DomainObjectA a : domainObjectARepository.getAllEntities()) 
            // do cool stuff
               
    

不幸的是,Weld 似乎不喜欢这种通用注入。在部署时,我收到以下错误:

state=Create: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Repository<DomainObjectA , PersistableUUID>] with qualifiers [@Default] at injection point [[field] @Inject sompackage.DomainObjectARepositoryTest.domainObjectARepository]

是我遗漏了什么还是他们只是忘记了实现泛型注入?据我了解通用的东西,无论如何它都会在编译后被删除 - 到目前为止,即使这在 guice3 中工作得很好。

亲切的问候,

avi

编辑:发现 garvin king 的 comment 表明此行为在规范中,但未在焊接中实现,(状态为 2009 年 6 月)

【问题讨论】:

很好的发现,关于评论:-) ty @jan。好吧,既然这个评论还不到两年,我希望weld 可以一直实施到现在 嗨@avithan,你是如何在 Guice 中做到这一点的?尤其是Provider&lt;E&gt; domainObjectProvider 部分。您是否为每个X 手动绑定Provider&lt;X&gt; 嗨@irreputable。如果我没记错的话,每种类型都有一个默认提供程序,它使用默认构造函数或使用@Inject 注释的构造函数。所以不需要绑定实体类型。 【参考方案1】:

这是一个相当长的评论,而不是对您问题的完整答案,但可能会为您指明正确的方向:

我很长一段时间以来一直在关注 seam-dev 和 Weld-dev 中的讨论,但我不记得曾经出现过这样的事情。所以我的猜测是,自从 Gavin 对此发表评论后,它就没有出现在议程上。

你可以做的比较容易验证这个假设:

(a) 获取对 BeanManager 的引用并查询它以获取相关的 bean 类型(或者只是将 Object 放在保存端),当然您必须在 DomainObjectARepositoryTest 中删除 @Inject为了启动应用程序。

(b) 注册一个扩展并收听ProcessBean 以了解部署期间出现的情况。这将是我建议的方式,您会发现更多信息here。

有了这个结果,您绝对应该能够判断是否有任何 bean 类型 Repository&lt;E, K extends Serializable &amp; Comparable&lt;K&gt;&gt; 闲逛:-)

如果您能在此处报告结果并考虑在否定情况下提交 Jira 问题,那就太好了。

【讨论】:

嗨@jan,感谢您的评论,我将在接下来的几天内尝试扩展方式。今天我刚刚与另一位 ee6 专业人士进行了交谈,他告诉我他正在使用开箱即用的通用注射(所以似乎正在为他工作)。也许是部署的问题(我正在使用 m2eclipse (+wtp-addon) 和 jboss eclipse 工具进行部署 - 也许他在那里使用了某种错误的配置文件 - 不知道) 嗨@jan,我终于明白了我在上面的示例中遗漏的愚蠢点:存储库是抽象的,没有针对存储库的具体实现。但是 - 如果 Repository 有多个实现,则依赖关系会变得模棱两可(即使类型参数不同),所以我必须手动从 Instance.iterator() 和一些 Class&lt;E&gt; getDomainObjectType() 方法中选择正确的。

以上是关于使用 CDI/Weld 注入通用 Bean的主要内容,如果未能解决你的问题,请参考以下文章

由于 CDI/Weld 中的 @Named,@ManagedBeans 在 JavaEE6 中过时了吗?

由于 CDI/Weld 中的 @Named,@ManagedBeans 在 JavaEE6 中过时了吗?

有继承时spring注入空指针问题

spring工具类中注入使用bean

spring注入bean的几种方式

CDI:由于多重继承和泛型抽象导致的属性注入问题