使用通用接口时不会调用 EJB 拦截器
给出以下代码,
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();
拦截器没有被调用(使用Glassfish 3.0.1)。
问题似乎是由于接口的编译类文件是
javap Foo
Compiled from "Foo.java"
public interface Foo{
public abstract java.lang.Object get();
}
BarImpl 的
javap BarImpl
Compiled from "BarImpl.java"
public class BarImpl extends java.lang.Object implements Bar{
public BarImpl();
public java.lang.String get();
public java.lang.Object get();
}
编译类文件,因此,当我们在内部调用时,
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 的普遍问题,还是 glassfish 的错误?有解决方法吗?或者我们应该完全放弃在我们的服务中使用仿制药吗?
Given the following code
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");
}
}
}
When we call a method on the remote,
Bar bar = context.lookup(Bar.class.getName());
bar.get();
or
Foo foo = context.lookup(Bar.class.getName());
foo.get();
the interceptor is not called (using Glassfish 3.0.1).
The problem seems to be due to the fact that the compiled class file for the interface is
javap Foo
Compiled from "Foo.java"
public interface Foo{
public abstract java.lang.Object get();
}
while for BarImpl it is
javap BarImpl
Compiled from "BarImpl.java"
public class BarImpl extends java.lang.Object implements Bar{
public BarImpl();
public java.lang.String get();
public java.lang.Object get();
}
So, when we call
Bar bar = ...;
bar.get();
Internally the method
public java.lang.Object get();
is called, which will delegate to
public java.lang.String get();
Interceptors seem to only be invoked, when the latter is called directly. When I change the interface Bar to
@Remote
public interface Bar extends Foo<String> {
@Override
String get();
}
The interceptor is called in the first call (bar.get()), but not in the second call (foo.get()). Defining the interceptors on the class level might fix the proplem, but is not an option in our case.
Do we do something wrong, or is this a general problem of java-ee-6, or is this a bug in glassfish? Is there a workaround? Or should we abandon using generics in our Services at all?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于我们处理的是 Java,类型擦除会在运行时接管,并且您的业务接口将只包含:
您正在将接口强制转换为
Bar
,这没问题(没有运行时类强制转换异常),但是业务接口被调用的方法是业务接口中存在的方法(因为这是客户端知道的唯一方法):Object get()
版本。此外,由于按照设计,仅拦截业务方法调用,因此在
String get()
上委托的Object get()
不会被拦截(它只是一个调用另一个方法的方法) )。这意味着,在您的初始场景中,您尝试拦截不属于业务接口的方法,因此永远不会被拦截。由于 EJB3 规范对于泛型确实不清楚(大多数情况下什么也没说),因此实现实际上将该部分委托给了 JVM。在这种情况下没有办法获得真正的通用支持,我想说你被类拦截器或改变你的业务接口所困扰。
Since we're dealing with Java, type erasure takes over during runtime and your business interface will ever only contain:
You are casting the interface to
Bar
and that's ok (no runtime class cast exception) but the business method that's being called is the one present in the business interface (as that's the only method the client knows about): theObject get()
version.Also, since, by design, only business method invocations are intercepted,
Object get()
delegating onString get()
does not get intercepted (it's just a method calling another method). This means that, in your initial scenario, you're trying to intercept a method that does not belong to the business interface and thus will never be intercepted.Since the EJB3 spec is really not clear about generics (mostly nothing is said), implementations really delegate that part on the JVM. There's no way of getting true generic support in this scenario and I would say you're stuck with class interceptors or changing your business interface.