Java EE拦截器更改返回类型

时间:2019-07-01 20:08:48

标签: java-ee jax-rs

我希望我的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),但是我怀疑它是否相关。

问题:

  1. 我在规范中缺少什么吗?还是有其他资源记录在案?

  2. 其他Java EE服务器在实现Interceptor方面是否有所不同?

2 个答案:

答案 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的返回类型更改为Objectjavax.ws.rs.core.Response或我自己的通用类型ReturnTypeHack<User>(类型擦除应使AugmentInterceptor返回一个ReturnTypeHack<MoreData>),但我不想为了在服务中保留Java返回类型信息。