我希望我的Java EE拦截器返回的类型不同于JAX-RS服务方法中定义的类型,但是我收到了ClassCastException
。这是一个最小的示例:
package com.example;
@Augment
@GET
@Path("/me")
@Produces(MediaType.APPLICATION_JSON)
public User me() {
return new User(...);
}
@Augment
@Interceptor
public class AugmentInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
runInNewResourceContext(resourceContext -> {
Object original = ctx.proceed(); // is a User object
return MoreData(original, resourceContext.getStats());
}); // context resources are cleaned-up after the lambda runs
}
}
java.lang.ClassCastException: com.example.MoreData不能为 转换为com.example.User com.example.Service $ Proxy $ _ $$ _ WeldSubclass.me(未知 源代码)〜[Service.class :?] sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 〜[?:1.8.0_201]在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 〜[?:1.8.0_201]在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 〜[?:1.8.0_201]在java.lang.reflect.Method.invoke(Method.java:498) 〜[?:1.8.0_201]在 org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda $ static $ 0(ResourceMethodInvocationHandlerFactory.java:52) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher $ 1.run(AbstractJavaResourceMethodDispatcher.java:124) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider $ TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:469) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:391) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:80) 〜[jersey-server.jar :?]在 org.glassfish.jersey.server.ServerRuntime $ 1.run(ServerRuntime.java:253) [jersey-server.jar :?]位于 org.glassfish.jersey.internal.Errors $ 1.call(Errors.java:248) [jersey-common.jar :?]在
我猜这是因为我的Java EE服务器是如何实现的:拦截器是在服务类的代理中调用的,并且代理需要返回代理类中定义的类型。
但是,从阅读JSR 318拦截器规范可以看出,我认为这不是设计限制。我没有在规范中看到有关返回类型/值的任何信息,也没有提到“代理”一词。
JSR 318拦截器规范:
https://jcp.org/en/jsr/detail?id=318
https://download.oracle.com/otndocs/jcp/interceptors-1_2A-mrel3-spec/
顺便说一句,我使用的是Payara 5.192(使用泽西岛2.29),但是我怀疑它是否相关。
问题:
我在规范中缺少什么吗?还是有其他资源记录在案?
其他Java EE服务器在实现Interceptor方面是否有所不同?
答案 0 :(得分:1)
一般,JSR-318拦截器无法更改方法的返回类型。尽管我无法在规范中明确找到它,但我很有信心这是由于Java语言的限制。例如。即使您说服拦截器机制返回不兼容的对象类型,被拦截方法的调用者仍将获得ClassCastException
。
从显示的代码中可以明显看出,您想丰富JAX-RS方法的返回资源。这完全可以通过另一种“拦截器” JAX-RS过滤器来完成。专门针对您的情况,ContainerResponseFilter
可以在调用resource方法后拦截该调用。它可以使用ContainerResponseContext.getEntity()
访问返回的实体,并用MoreData
将其替换为ContainerResponseContext.setEntity(Object)
对象。
示例代码:
@Provider
@Augment
public class AugmentFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
Object original = responseContext.getEntity();
responseContext.setEntity(MoreData(original));
}
}
请看一下规范,以了解绑定批注对JAX-RS的工作方式,您可能需要消除一些扣留,但原则仍然存在。
答案 1 :(得分:0)
我意识到我可以将me
的返回类型更改为Object
或javax.ws.rs.core.Response
或我自己的通用类型ReturnTypeHack<User>
(类型擦除应使AugmentInterceptor返回一个ReturnTypeHack<MoreData>
),但我不想为了在服务中保留Java返回类型信息。