2016-08-23 99 views
1

我正在构建一个应用程序,该应用程序使用JPA(EclipseLink)提供JAX-RS REST服务。在通过JSON公开用户实体时,我在某些字段(例如密码字段)上使用@XmlTransient注释来将其隐藏在JSON表示中。发送创建或更新(POST/PUT)操作时,我想再次填充缺失的字段,以便JPA将正确执行操作。将无状态会话Bean注入自定义JsonDeserializer失败

我目前的做法是我有一个自定义JsonDeserializer用于反序列化用户并添加缺少的字段。为此我想注入(使用@Inject)处理JPA-stuff的UserFacadeREST bean。然而,这个注入失败,并且bean实例是null(这当然会导致NullPointerException)。

UserFacadeREST bean是annoted如下:

@Stateless 
@LocalBean 
@Path(UserFacadeREST.PATH) 
public class UserFacadeREST extends AbstractFacade<User> { 
    //... 
} 

UserDeserilizer(定制JsonDeserializer):

public class UserDeserializer extends JsonDeserializer<User> { 

    @Inject 
    private UserFacadeREST userFacade; 

    @Override 
    public User deserialize(JsonParser parser, DeserializationContext context) throws IOException, 
     JsonProcessingException { 
    JsonNode node = parser.getCodec().readTree(parser); 
    int userId = (Integer) ((IntNode) node.get("userID")).numberValue(); 
    System.out.println(userId); 
    User user = userFacade.find(userId); // This line produces the NullPointerException 
    return user; 
    } 

} 

然后我在我的用户实体使用带有@JsonDeserialize

@Entity 
@Table(name = "User") 
@XmlRootElement 
@JsonDeserialize(using = UserDeserializer.class) 
public class User implements Serializable { 
    // ... 
} 

我在我的W中包含了一个bean.xml文件带有bean-discovery-mode的EB-INF文件夹设置为all。我错过了什么?

+0

您只能@Inject入CDI容器管理对象。由于'UserDeserializer'没有被声明为托管bean,因此不应该发生依赖注入,因为容器不会为您提供此服务。 – scottb

+0

谢谢。那么如何声明我的UserDeserializer是一个托管bean?我试过'@ ApplicationScoped'和'@ Singleton',但都没有工作... – Severin

回答

3

乔恩彼得森指出我正确的方向。某种程度上,我终于选择实施'hackish'解决方案。请注意,这里基本上有两个选项(如果你知道另一个选项,请告诉我!)。短版:

  1. hackish的解决方案(在我选择的解决方案):注入编程方式使用javax.enterprise.inject.spi.CDI.current().select(UserFacadeRest.class).get()一个bean作为问题由Jon提到accepted answer
  2. 更好(干净)解决方案描述(但也更精细):重新设计逻辑以填充反序列化后遗漏的字段,如Jon所建议的。

所以对于我的问题,解决方法如下所示:

1。

import javax.enterprise.inject.spi.CDI; 

public class UserDeserializer extends JsonDeserializer<User> { 

    private final UserFacadeREST userFacade = 
     CDI.current().select(UserFacadeREST.class).get(); 

    // Rest as before 
} 

2。在这种情况下,我JsonDeserializerdeserialize方法我会构建一个刚刚保存的用户ID的用户。在每个请求方法中,我都必须检查所有用户,并通过调用EntityManager.find(User.class, user.getUserID())来替换实际用户。这意味着在业务逻辑中需要付出更多努力,因为您必须记住,每次需要在请求方法中使用User时,首先必须执行查询以获取“完整”User对象。在第一种解决方案中,这个查询对业务逻辑是隐藏的,已经发生在JsonDeserializer

public class UserDeserializer extends JsonDeserializer<User> { 

    @Override 
    public User deserialize(JsonParser parser, DeserializationContext context) throws IOException, 
     JsonProcessingException { 
    JsonNode node = parser.getCodec().readTree(parser); 
    int userId = (Integer) ((IntNode) node.get("userID")).numberValue(); 
    return new User(userId); // Placeholder User object containing only the user ID, needs to be replaced in business logic 
    } 

} 
1

我不是超级熟悉CDI,但一些快速Google'ing使我相信,bean-discovery-mode要么是allannotated,或nonetrue不是一个有效的值)。 Reference

如果这样不能解决问题,那么Spring可能会遇到同样的问题:您必须将UserDeserializer声明为要应用依赖注入的bean。

编辑:刚才发现这个other question这是基本上相同的问题,你有。

最终,您可能需要重新设计逻辑,以便在反序列化后调用userFacade

+0

对不起乔恩,你是对的。我实际上使用了'all',只是在写问题时混淆了它。我刚刚检查过,使用'true'时该项目甚至没有编译。 那么如何将我的'UserDeserializer'声明为一个bean?当然有一些注释,但是哪一个? – Severin

+0

虽然给了这一秒,但我其实不认为这可以完成。 UserDeserializer的实例将由Jackson创建,而不是由CDI创建。这意味着CDI注释(前@ @ Inject)不会被处理。我刚刚发现了另一个问题,我将把它写入我的编辑答案: –

+0

感谢乔恩,您指出我正确的方向。以编程方式注入UserFacadeREST的实例适用于我,所以我选择了这种方法,因为它需要较少的工作量,我们在一个地方“填充”了缺失的字段,并从业务逻辑中抽象出来。但我明白,这不是最好的方法。我在回答中试图强调这一点,并提及两种方法,所以我会将我的标记标记为已接受的标记,以防其他人更喜欢坚持注射。 – Severin