Событие пожара с типом времени выполнения подкласса

Я хочу запустить событие в CDI, тип которого я могу определить только во время выполнения. Например, предположим, что есть некоторый интерфейс A с реализацией классов AA и AB. У меня есть два наблюдателя:

public void observeAA(@Observes AA aa) {
}

public void observeAA(@Observes AB ab) {
}

Затем какой-то производитель событий:

@Inject @Any
private Event<A> event;

public A getPayload();

public void fire() {
    this.event.fire(getPayload());
}

Это не работает, потому что A не является ни подтипом AA, ни AB (наоборот). Я заметил, что есть метод select, который принимает подтип:

public <U extends T> Event<U> select(Class<U> subtype, Annotation... qualifiers);

Однако для этого требуется правильно параметризованный объект Class, который (поправьте, если я ошибаюсь) я не могу построить во время выполнения.

Есть ли какое-либо решение или мне придется использовать квалификаторы (возможно, аннотацию с методом Class<?>)?


person Artefacto    schedule 04.08.2011    source источник


Ответы (3)


В итоге я использовал квалификатор с элементом Class<?>.

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


public class Dispatcher {

    @Inject @Any
    private Event<A> event;

    public void fireEvent(A a) {
            this.event.select(
                    getTypeAnnotation(
                    a.getClass())).fire(a);
    }

    public static EventType getTypeAnnotation(
            final Class<?> type) {
        return (EventType) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{EventType.class},
                new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method,
                    Object[] args) throws Throwable {
                if (method.equals(
                        EventType.class.getMethod("value"))) {
                    return type;
                } else if (method.equals(Annotation.class.getMethod(
                        "annotationType"))) {
                    return EventType.class;
                } else if (method.getName().equals("hashCode")) {
                    return 127 * "value".hashCode() ^ type.hashCode();
                } else if (method.getName().equals("equals")) {
                    return (args[0] instanceof EventType &&
                            ((EventType)args[0]).value()
                            .equals(type));
                }
                return null;
            }
        });
    }
}

public class X {
    public void observeA(
            @Observes @EventType(AA.class) A a) {
    ...

ИЗМЕНИТЬ

Это более простой способ создания экземпляра аннотации:

public abstract static class ConfigTypeAnnotation
        extends AnnotationLiteral<ConfigType>
        implements ConfigType { }

public static ConfigType getConfigTypeAnnotation(final Class<?> type) {
    return new ConfigTypeAnnotation() {
        @Override
        public Class<?> value() {
            return type;
        }
    };
}
person Artefacto    schedule 04.08.2011

Почему бы тебе не использовать

public void observeA(@Observes A a) {
}

в котором вы решаете, что делать в соответствии с классом реализации «а»?

public void observeA(@Observes A a) {
    if (a instanceof AA)
    {
     ...
    }
    else
    ...
}
person Antoine Sabot-Durand    schedule 04.08.2011
comment
Потому что я не могу централизовать эту логику. Наблюдателю нужно изменить приватное состояние и т. д. Я мог бы сделать if (!(a instanceof AA)) return;, но мне пришлось бы поместить это в каждого наблюдателя. - person Artefacto; 04.08.2011

У меня было аналогичное требование, и в итоге я ввел BeanManager для запуска события.

person Sam Lawrance    schedule 16.08.2014