Перехватчики EJB не вызываются при использовании универсальных интерфейсов - PullRequest
4 голосов
/ 17 марта 2011

Учитывая следующий код

public interface Foo<T> {
    T get();
}

@Remote
public interface Bar extends Foo<String> {
}

@Stateless
public class BarImpl implements Bar {
    @Interceptors(ExceptionInterceptor.class)
    public String get() {
        throw new RuntimeException("not implemented");
    }
}

public class ExceptionInterceptor {
    @AroundInvoke
    public Object convertExceptionForExternalSystem(InvocationContext ctx) throws RuntimeException, Error {
        try
        {
            return ctx.proceed();
        }
        catch (Throwable e)
        {
            if (e instanceof Error)
                throw new Error("Changed");
            throw new RuntimeException("Changed");
        }
    }
}

Когда мы вызываем метод на пульте,

Bar bar = context.lookup(Bar.class.getName());
bar.get();

или

Foo foo = context.lookup(Bar.class.getName());
foo.get();

перехватчик не вызывается (using Glassfish 3.0.1).

Проблема, по-видимому, связана с тем, что скомпилированный файл класса для интерфейса имеет вид

javap Foo
Compiled from "Foo.java"
public interface Foo{
    public abstract java.lang.Object get();
}

, а для BarImpl -

* 1016.*

Итак, когда мы вызываем

Bar bar = ...;
bar.get();

Внутренне вызывается метод

public java.lang.Object get(); 

, который делегирует

public java.lang.String get();

Перехватчики кажутся тольковызываться, когда последний вызывается напрямую.Когда я изменяю интерфейс Bar на

@Remote
public interface Bar extends Foo<String> {
    @Override
    String get();
}

Перехватчик вызывается при первом вызове (bar.get ()), но не при втором вызове (foo.get ()).Определение перехватчиков на уровне класса может исправить проблему, но в нашем случае это не вариант.

Мы делаем что-то не так, или это общая проблема java-ee-6, или этоошибка в стеклянной рыбе?Есть ли обходной путь?Или мы вообще должны отказаться от использования дженериков в наших Сервисах?

1 Ответ

3 голосов
/ 19 декабря 2011

Поскольку мы имеем дело с Java, стирание типов вступает во владение во время выполнения, и ваш бизнес-интерфейс будет содержать только:

public Object get();

Вы переводите интерфейс на Bar, и это нормально (без времени выполненияисключение приведения класса), но вызываемый бизнес-метод - это тот, который присутствует в бизнес-интерфейсе (поскольку это единственный метод, о котором знает клиент): Object get() версия.

Кроме того, поскольку, по замыслу,перехватываются только вызовы бизнес-методов, Object get() делегирование на String get() не перехватывается (это просто метод, вызывающий другой метод).Это означает, что в вашем начальном сценарии вы пытаетесь перехватить метод, который не принадлежит бизнес-интерфейсу и, следовательно, никогда не будет перехвачен.

Поскольку спецификация EJB3 действительно не совсем понятна в обобщениях (в основномничего не сказано), реализации действительно делегируют эту часть в JVM.В этом сценарии невозможно получить истинную общую поддержку, и я бы сказал, что вы застряли с перехватчиками классов или изменили свой бизнес-интерфейс.

...