0

问题:

给定 REST 端点:

@Path("/companies")
@Stateless
public class CompanyService{

    @EJB
    private CompanyEjb ejb;

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Company update(Company company){
        return ejb.update(company);
    }

}

提交PUT请求后,JAX-RS 预计会返回更新后的公司,例如

{
    "id": 1,
    "name": "Company",
    "departments": [1,2]
}

但是,只1返回主键 , 。第二个请求触发异常:

Warning:   StandardWrapperValve[[RestApplicationClass]: Servlet.service() for servlet RestApplicationClass threw exception
java.lang.NoSuchMethodException: java.lang.Long.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance$1(ReflectionUtils.java:188)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:186)
    at org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:92)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:62)
    at org.eclipse.yasson.internal.serializer.AdaptedObjectDeserializer.deserialize(AdaptedObjectDeserializer.java:94)
    at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:57)
    at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50)
    at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45)
    at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:85)

尽管我没有要适应的公司,但似乎已应用了适配器

语境

配置是:

  • Payara 5.0.0.Alpha3(因此是 Yasson 1.0)
  • Java EE 8(因此是 JAX-RS 2.1、JSON-B 1.0 和 JSON-P 1.1)
  • 应用程序以 EAR 格式打包
  • 使用 Postman 提交的 REST 请求

两个实体CompanyDepartment通过@OneToMany关系链接。两个实体都有一个主键id和一个名称name。为了避免 JSON 无限循环,@ManyToOne公司的部门有一个适配器:

public class Department{
    // ...
    @ManyToOne // JPA stuff irrelevant here
    @JsonbTypeAdapter(CompanyAdapter.class)
    private Company company;
    // ...
}

使用(简化的)适配器:

public class CompanyAdapter implements JsonbAdapter<Company, Long>{

    @EJB
    private CompanyEjb ejb;

    @Override
    public Long adaptToJson(Company orgnl) throws Exception {
        return orgnl != null ? orgnl.getId() : null;
    }

    @Override
    public E adaptFromJson(Long adptd) throws Exception {
        return ejb.findById(adptd);
    }
}

解决方法:

最初,我认为一旦定义了适配器,它们就会被应用到任何地方,类似于带有autoApply = true. 这让我无处可去

起初,我考虑使用 Yasson 的最新版本(1.0.2-SNAPSHOT)来解决这个问题。为此,我需要告诉 Payara 加载我想要的 Yasson。所以glassfish-web.xml修改如下:

<glassfish-web-app error-url="">
    ...
    <!-- <class-loader delegate="true"/> -->
    <class-loader delegate="false"/>
    ...
</glassfish-web-app>

根据Payara 文件

但似乎仅将class-loader授权切换为 false 就足够了。

=> 为什么?

附属问题:

由于一些热部署问题,我需要执行以下操作来测试我的更改:

  1. 做出改变
  2. 构建和部署
  3. 部署后重启服务器

如果有人知道为什么热部署/简单部署会修改 JAX-RS 适配器行为,我期待答案

4

1 回答 1

1

只是为了在这里发回 SO,OP 在 Yasson 上提出了一个详细的GitHub 问题,根本原因是 Yasson 缓存了对象模型(因此它不必每次都重新扫描对象模型)。在 Yasson 由容器(例如 JAX-RS 运行时)提供的应用程序服务器环境中,这种缓存会导致问题,当用户更新他们的应用程序时,只有他们的应用程序类会重新加载,但 Yasson 和它的缓存会保持加载状态。

短期修复

目前的解决方法是在更改 JSON-B 数据类时重新启动应用服务器。

长期

我将看看Yasson 和/或 JSON-B 规范是否可以定义某种“清除缓存”挂钩。这里的想法是当应用服务器热更新应用程序时,它可以调用这个“清除缓存”挂钩来从应用程序中清除存储的对象模型。

于 2018-12-11T04:28:54.943 回答