Расширения CDI: Могу ли я предоставить один интерфейс в двух областях? - PullRequest
1 голос
/ 05 октября 2011

У меня есть интерфейс в рамках модульного тестирования: CDIMocker.В настоящее время я использую перехватчик, чтобы разрешить насмешку в контейнере CDI.Это эксперимент - один из нескольких подходов к модульному тестированию, который я рассматриваю.(Другим основным претендентом является использование инжектора конструктора и метода для всех bean-компонентов и модульного теста вне CDI - в этом случае эта работа становится больше обучающим упражнением в расширениях CDI).

У меня есть две настраиваемые области - TestClassScoped иTestMethodScoped.Мой пользовательский бегун JUnit4 упаковывает соответствующие блоки Class и Method в инструкции, которые запускают и останавливают эти области по мере необходимости.Он также запускает экземпляр Weld-SE, если это необходимо.Он знает, находится ли он в CDI, потому что расширение запоминает.

Интерфейс mocker одинаков, где бы он ни использовался.Было бы неплохо показать его в обеих областях, чтобы я мог

 // 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 может жить только в одном классе, поэтому будет ли это неправильно?

Есть предложения о том, что лучше?

Спасибо - Ричард

(Я бы хотел открыть исходный код результата, но я сделал достаточно в рабочее время, и мне пришлось бы об этом спрашивать, что маловероятно. Бизнес-аргумент будет публичным рассмотрением. Сейчас я использую Interceptor с тем недостатком, что он долженоставайтесь на месте, но подумайте, смогу ли я чего-то достичь, перехватывая жизненный цикл bean-компонента в расширении. Мы можем использовать альтернативы для таких вещей, как слой comms, который общается с нашим устаревшим сервером приложений, но для некоторых вещей отдельный модульный тест требует пользовательскогомакет и альтернативы слишком глобальны.)

1 Ответ

1 голос
/ 05 октября 2011

Я создал

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

В настоящее время у меня есть две реализации Бина.Соответствующие (необычные) части:

/**
 * 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();
}

Тип - это мой интерфейс 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();
}

Решение использует квалификаторы, поэтому я полагаю, что теперь оно "Правильно",Я предполагаю, что могу использовать управление жизненным циклом таким образом?

Мой тестовый класс (который мой Бегун создает с помощью 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;
  • Ричард
...