【问题标题】:CDI Extensions: Can I expose one interface in two scopes?CDI 扩展:我可以在两个范围内公开一个接口吗?
【发布时间】:2011-10-05 08:51:53
【问题描述】:

我在单元测试框架中有一个接口:CDIMocker。我目前正在使用拦截器来允许在 CDI 容器中进行模拟。这是一个实验——我正在考虑的几种单元测试方法之一。 (另一个主要竞争者是在 CDI 之外对所有 bean 和单元测试使用构造函数和方法注入 - 在这种情况下,这项工作更像是 CDI 扩展中的学习练习)。

我有两个自定义范围 - TestClassScoped 和 TestMethodScoped。我的 JUnit4 自定义运行器将适当的 Class 和 Method 块包装在根据需要启动和停止这些范围的语句中。如果需要,它还会启动 Weld-SE 的实例。它知道它是否在 CDI 中,因为扩展会记住。

无论我们在哪里使用,模拟程序界面都是相同的。在两个范围内公开它会很好,所以我可以

 // Sadly Static Injection currently doesn't work, but if it did
 @Inject @TestClassScoped
 private static CdiMocker s_classScopedMocker

 @Inject @TestMethodScoped
 private CdiMocker m_methodScopedMocker

还有其他明显的方法。我目前在 CDI 外部的单例上有一个工厂方法,它可以返回这些实例中的任何一个(ThreadLocal),或者创建一个新的短期实例。我还成功地创建了两个具体的类并在它们上声明了不同的范围。

我尝试过使用上面注释的生产者方法,但没有运气。也许是一个简单的错误,也许是一个误解。

@Produces @TestClassScoped
public CdiMocker getClassScopedMockerForCdi()
{
    return getTestClassContext().getMocker();
}

@Produces @TestMethodScoped
public CdiMocker getMethodScopedMockerForCdi()
{
    return getTestMethodContext().getMocker();
}

我认为从 CDI 文档的某些部分可以像我所做的那样在注入点上声明作用域,但我注意到 Instance 接口不允许我使用作用域注释选择()所以也许是错了。

我可以提供两个限定词。一个注解可以同时是限定符和作用域吗?

另一个想法是让我的扩展提供两个 Bean,它们都公开相同的类,但在不同的范围内。他们还可以提供自定义 create() 和 destroy(),因为 CdiMocker 实例由我的两个自定义上下文管理。我对 CDI 的印象是,一个给定的 Class 只能存在于一个 Scope 中,那么这是错误的吗?

有什么最好的建议吗?

谢谢 - 理查德

(我很想将结果开源,但我已经在工作时间上做了足够多的工作,所以我不得不问,所以不太可能。商业论点将是公开审查。我现在使用拦截器,但它的缺点是留在原地,但想知道我是否可以通过拦截扩展中的 bean 生命周期来实现某些东西.我们可以将 Alternatives 用于与我们的遗留应用程序服务器通信的通信层之类的事情,但对于某些事情,单个单元测试需要一个自定义 mock 和 Alternatives 过于全球化。)

【问题讨论】:

    标签: unit-testing jakarta-ee cdi


    【解决方案1】:

    我已经创建了

    @Qualifier
    @Target({TYPE, METHOD, PARAMETER, FIELD})
    @Retention(RUNTIME)
    @Documented
    public @interface Scoped
    {
        Class<? extends Annotation> value();
    }
    

    我目前有两个 Bean 实现。相关(不寻常)部分是:

    /**
     * A Bean<> implementation for the CdiMocker beans
     */
    class MockerBean implements Bean<CdiMocker>
    {
        private final class ScopedAnnotation extends AnnotationLiteral<Scoped> implements Scoped
        {
            private static final long serialVersionUID = 1L;
            public Class<? extends Annotation> value() { return m_context.getScope(); }
        }
    
        private final CdiMockContextControl m_context;
    
        public MockerBean(CdiMockContextControl context)
        {
              m_context = context;
        }
    

    bean 类是 CdiMocker.class

        @Override
        public Class<?> getBeanClass()
        {
              return CdiMocker.class;
        }
    

    限定符包括我上面定义的 ScopedAnnotation。我还包括了 Default 和 Any。也许我需要删除这些?

    范围由我的 CdiMockContextControl 接口返回。

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

    Type 是我的 CdiMocker 接口

    @Override
    public Set<Type> getTypes()
    {
        Set<Type> types = new HashSet<Type>();
        types.add(CdiMocker.class);
        types.add(Object.class);
        return types;
    }
    

    因为生命周期是在其他地方管理的,所以我返回现有的。

    @Override
    public CdiMocker create(CreationalContext<CdiMocker> arg0)
    {
        return m_context.getMocker();
    }
    

    ...不要破坏它。

    @Override
    public void destroy(CdiMocker instance, CreationalContext<CdiMocker> ctx)
    {
        // It is managed by the Context, so I must not destroy it here.
        ctx.release();
    }
    

    解决方案是使用限定符,所以我想它现在是“正确的”。我假设我可以通过这种方式使用生命周期管理?

    我的测试类(我的 Runner 使用 CDI 实例化)有

    /**
     * My CDI Extension makes a Mocking Context available in the Test Method Scope.
     * This will be created before every test method, then destroyed afterwards.
     */
    @Inject @Scoped(TestMethodScoped.class)
    private CdiMocker m_testMethodMocker;
    
    • 理查德

    【讨论】:

      猜你喜欢
      • 2013-11-02
      • 2015-10-14
      • 2018-12-01
      • 1970-01-01
      • 2011-09-29
      • 2010-10-13
      • 2021-06-19
      • 2019-10-22
      相关资源
      最近更新 更多