简体   繁体   English

EJB和CDI bean序列化的最佳实践

[英]Best practice for serialization for EJB and CDI beans

I have not yet experienced any serialization-related issues. 我还没有遇到任何序列化相关的问题。 But PMD and Findbugs detect a bunch of potential problems regarding seriazation. 但是PMD和Findbugs发现了一系列关于序列化的潜在问题。 A typical case is an injected logger that is being detected as non-serializable. 典型情况是注入的记录器被检测为不可序列化。 but there are many more - EntityManager and several CDI beans. 但还有更多 - EntityManager和几个CDI bean。

I have not found any best practices on how to deal with serialization correctly. 我没有找到任何关于如何正确处理序列化的最佳实践。

  • will the fields, injected by @Inject and @PersistenceContext be reinjected on deserialization? @Inject@PersistenceContext注入的字段重新注入反序列化?
  • should they be marked as transient ? 它们应该标记为transient吗?
  • or should I just ignore/switch off the code checks? 或者我应该忽略/关闭代码检查?
  • should I really provide accessors to all those fields as PMD advises? PMD建议我应该真正提供所有这些领域的访问者吗?

I realize this is an old question, but I believe the only answer provided is incorrect. 我意识到这是一个老问题,但我相信提供的唯一答案是不正确的。

will the fields, injected by @Inject and @PersistenceContext be reinjected on deserialization? 将@Inject和@PersistenceContext注入的字段重新注入反序列化?

No, they will not. 不,他们不会。 I personally experienced this with JBoss in a clustered environment. 我个人在集群环境中使用JBoss体验过这一点。 If the bean is passivation capable, then the container must inject a serializable proxy. 如果bean具有钝化能力,那么容器必须注入可序列化的代理。 That proxy gets serialized and deserialized. 该代理被序列化和反序列化。 Once deserialized, it will locate the proper injection and rewire it. 一旦反序列化,它将找到正确的注入并重新连接它。 However, if you mark the field transient, the proxy is not serialized and you will see NPEs when the injected resource is accessed. 但是,如果将字段标记为瞬态,则代理不会序列化,并且在访问注入的资源时您将看到NPE。

It should be noted that the injected resource or bean does not have to be Serializable, because the proxy will be. 应该注意,注入的资源或bean不必是Serializable,因为代理将是。 The only exception is for @Dependent scoped beans which have to be serializable or the injection transient. 唯一的例外是@Dependent范围的bean,它们必须是可序列化的或者是注入瞬态的。 This is because a proxy is not used in this case. 这是因为在这种情况下不使用代理。

should they be marked as transient? 它们应该标记为瞬态吗?

No, see above. 不,见上文。

or should I just ignore/switch off the code checks? 或者我应该忽略/关闭代码检查?

This is up to you, but it is what I would do. 这取决于你,但这就是我要做的。

should I really provide accessors to all those fields as PMD advises? PMD建议我应该真正提供所有这些领域的访问者吗?

No, I would not. 我不会。 In our projects, we disable this check when we know we are using CDI. 在我们的项目中,当我们知道我们正在使用CDI时,我们会禁用此检查。

This answer will detail the serialization/passivation semantics for EJB 3.2 ( JSR 345 ), JPA 2.1 ( JSR 338 ) and CDI 1.2 ( JSR 346 ). 这个答案将详细介绍EJB 3.2( JSR 345 ),JPA 2.1( JSR 338 )和CDI 1.2( JSR 346 )的序列化/钝化语义。 Noteworthy is that the Java EE 7 umbrella specification ( JSR 342 ), the Managed Beans 1.0 specification ( JSR 316 ) and the Commons Annotations specification 1.2 ( JSR 250 ) does not have anything to say that is of interest to us in regards to serialization/passivation. 值得注意的是,Java EE 7伞规范( JSR 342 ),Managed Beans 1.0规范( JSR 316 )和Commons Annotations规范1.2( JSR 250 )没有任何关于序列化/我们感兴趣的内容/钝化。

I will also touch on the topic of static code analyzers. 我还将讨论静态代码分析器的主题。

EJB EJB

Relevant sections are "4.2 Conversational State of a Stateful Session Bean" and "4.2.1 Instance Passivation and Conversational State". 相关章节是“4.2有状态会话Bean的会话状态”和“4.2.1实例钝化和会话状态”。

@Stateless and @Singleton instances are never passivated. @Stateless@Singleton实例永远不会被钝化。

@Stateful instances may be passivated. @Stateful实例可能被钝化。 Since EJB 3.2, the class developer can opt-out from passivation using @Stateful(passivationCapable=false) . 从EJB 3.2开始,类开发人员可以使用@Stateful(passivationCapable=false)选择退出钝化。

The EJB specification explicitly notes that references to things such as UserTransaction , EntityManagerFactory and container-managed EntityManager are taken care of by the container. EJB规范明确指出,容器会处理对诸如UserTransactionEntityManagerFactory和容器管理的EntityManager类的事物的引用。 A @Stateful instance which uses an extended persistence context will not be passivated unless all entities in the persistence context and the EntityManager implementation is serializable. 除非持久化上下文和EntityManager实现中的所有实体都是可序列化的,否则不会钝化使用扩展持久性上下文的@Stateful实例。

Please note that an application-managed EntityManager always uses an extended persistence context. 请注意,应用程序管理的EntityManager始终使用扩展的持久性上下文。 Also, a @Stateful instance is the only type of EJB session instance which may use a container-managed EntityManager with an extended persistence context. 此外,@ Stateful实例是EJB会话实例的唯一类型,它可以使用容器管理的EntityManager和扩展的持久化上下文。 This persistence context would be bound to the life cycle of the @Stateful instance instead of one single JTA transaction. 此持久性上下文将绑定到@Stateful实例的生命周期,而不是单个JTA事务。

The EJB specification does not explicitly address what happens to a container-managed EntityManager with an extended persistence context. EJB规范没有明确地解决具有扩展持久化上下文的容器管理的EntityManager所发生的情况。 My understanding is this: If there is an extended persistence context, then this guy must be deemed serializable or not according to the rules defined previously and if it is, then passivation proceeds. 我的理解是这样的:如果有一个扩展的持久化上下文,那么根据之前定义的规则,这个人必须被认为是可序列化的,如果是,则进行钝化。 If passivation proceeds, then the @Stateful class developer need only concern himself with references to application-managed entity managers. 如果钝化继续进行,那么@Stateful类开发人员只需关注自己对应用程序管理的实体管理器的引用。

The EJB specification does not specify what happens to transient fields other than describing an assumption we as developers should make. 除了描述开发人员应该做出的假设之外,EJB规范没有规定瞬态字段会发生什么。

Section 4.2.1 says: 第4.2.1节说:

The Bean Provider must assume that the content of transient fields may be lost between the PrePassivate and PostActivate notifications. Bean Provider必须假设在PrePassivate和PostActivate通知之间可能会丢失瞬态字段的内容。

[...] [...]

While the container is not required to use the Serialization protocol for the Java programming language to store the state of a passivated session instance, it must achieve the equivalent result. 虽然容器不需要使用Java编程语言的序列化协议来存储钝化会话实例的状态,但它必须实现相同的结果。 The one exception is that containers are not required to reset the value of transient fields during activation. 一个例外是容器在激活期间不需要重置瞬态字段的值。 Declaring the session bean's fields as transient is, in general, discouraged. 一般来说,不鼓励将会话bean的字段声明为瞬态。

Requiring the container to "achieve the equivalent result" as Javas serialization protocol at the same time leaving it totally unspecified as to what happens with transient fields is quite sad, to be honest. 要求容器“实现与Javas序列化协议相同的结果”,同时让它完全没有说明瞬态字段会发生什么,这是非常可悲的,说实话。 The take-home lesson is that nothing should be marked transient. 带回家的教训是,任何东西都不应该被标记为短暂的。 For fields that the container can not handle, use @PrePassivate to write a null and @PostActivate to restore. 对于容器无法处理的字段,请使用@PrePassivate写入null@PostActivate进行还原。

JPA JPA

The word "passivation" does not occur in the JPA specification. JPA规范中没有出现“钝化”一词。 Nor does JPA define serialization semantics for types such as EntityManagerFactory , EntityManager , Query and Parameter . JPA也没有为EntityManagerFactoryEntityManagerQueryParameter等类型定义序列化语义。 The only sentence in the specification relevant to us is this (section "6.9 Query Execution"): 规范中与​​我们相关的唯一一句是(“6.9查询执行”部分):

CriteriaQuery, CriteriaUpdate, and CriteriaDelete objects must be serializable. CriteriaQuery,CriteriaUpdate和CriteriaDelete对象必须是可序列化的。

CDI CDI

Section "6.6.4. Passivating scopes" define a passivating scope as a scope explicitly annotated @NormalScope(passivating=true) . “6.6.4。钝化作用域”一节将钝化作用域定义为显式注释@NormalScope(passivating=true)的作用域。 This property defaults to false. 此属性默认为false。

One implication is that @Dependent - which is a pseudo scope - is not a passivation capable scope. 一个含义是@Dependent - 这是一个伪范围 - 不是一个具有钝化能力的范围。 Also noteworthy is that javax.faces.view.ViewScoped is not a passivation capable scope which for whatever reason the majority of Internet seems to believe. 另外值得注意的是, javax.faces.view.ViewScoped不是一个具有钝化能力的范围,无论出于何种原因,大多数互联网似乎都相信这一范围。 For example, section "17-2. Developing a JSF Application" in the book "Java 9 Recipes: A Problem-Solution Approach". 例如,“Java 9 Recipes:A Problem-Solution Approach”一书中的“17-2。开发JSF应用程序”一节。

A passivation capable scope requires that instances of classes declared "with the scope are passivation capable" (section "6.6.4. Passivating scopes"). 具有钝化能力的范围要求声明“具有范围的类的实例具有钝化能力”(“6.6.4。钝化范围”部分)。 Section "6.6.1. Passivation capable beans" define such an object instance simply as one being transferable to secondary storage. “6.6.1。具有钝化功能的bean”一节将这样的对象实例定义为可转移到二级存储的对象实例。 Special class- annotations or interfaces are not an explicit requirement. 特殊的类注释或接口不是明确的要求。

Instances of EJB:s @Stateless and @Singleton are not "passivation capable beans". EJB实例:@Stateless和@Singleton不是“具有钝化功能的bean”。 @Stateful may be (stateful is the only EJB session type which it makes sense to let CDI manage the life cycle of - ie, never put a CDI scope on a @Stateless or @Singleton). @Stateful可能是(有状态是唯一允许CDI管理生命周期的EJB会话类型 - 即,永远不会将CDI范围放在@Stateless或@Singleton上)。 Other "managed beans" are only "passivation capable beans" if they and their interceptors and decorators are all serializable. 如果它们及其拦截器和装饰器都是可序列化的,那么其他“托管bean”只是“具有钝化功能的bean”。

Not being defined as a "passivation capable bean" does not mean that things such as stateless, singleton, EntityManagerFactory, EntityManager, Event and BeanManager can not be used as dependencies inside a passivation capable instance that you author. 未被定义为“具有钝化功能的bean”并不意味着诸如无状态,单例,EntityManagerFactory,EntityManager,Event和BeanManager之类的东西不能用作您创作的具有钝化功能的实例中的依赖项。 These things are instead defined as "passivation capable dependencies" (see section "6.6.3. Passivation capable dependencies" and "3.8. Additional built-in beans"). 这些东西被定义为“具有钝化能力的依赖性”(参见“6.6.3。能够依赖钝化的依赖性”和“3.8。附加内置bean”一节)。

CDI make these depedencies passivation capable through the use of passivation capable proxies (see last bulleted item in section "5.4. Client proxies" and section "7.3.6. Lifecycle of resources"). CDI通过使用具有钝化能力的代理来使这些依赖性能够被钝化(参见“5.4。客户端代理”一节中的最后一个项目符号项和“7.3.6。资源的生命周期”一节)。 Please note that for Java EE resources such as the EntityManagerFactory and EntityManager to be passivation capable, they must be declared as a CDI producer field (section "3.7.1. Declaring a resource"), they do not support any other scope than @Dependent (see section "3.7. Resources") and they must be looked up on the client-side using @Inject. 请注意,对于要使其具有钝化能力的Java EE资源(例如EntityManagerFactory和EntityManager),必须将它们声明为CDI生成器字段(“3.7.1。声明资源”一节),它们不支持除@Dependent之外的任何其他范围(参见“3.7。资源”一节),必须使用@Inject在客户端查找它们。

Other @Dependent instances - albeit not declared with a normal scope and not required to be fronted by a CDI "client proxy" - can also be used as a passivation capable dependency if the instance is transferable to secondary storage, ie, serializable. 其他@Dependent实例 - 虽然没有声明具有正常范围且不需要由CDI“客户端代理”前端 - 如果实例可以转移到辅助存储,即可序列化,也可以用作具有钝化能力的依赖关系。 This guy will be serialized together with the client (see last bulleted item in section "5.4. Client proxies"). 这个人将与客户端一起序列化(请参阅“5.4。客户端代理”部分中的最后一个项目符号项)。

To be perfectly clear and to provide a few examples; 要非常明确并提供一些例子; a @Stateless instance, a reference to an EntityManager produced by CDI and a serializable @Dependent instance can all be used as instance fields inside your class annotated with a passivation capable scope. @Stateless实例,对CDI生成的EntityManager的引用和可序列化的@Dependent实例都可以用作类中的实例字段,并使用具有钝化功能的范围进行注释。

Static code analyzers 静态代码分析器

Static code analyzers are stupid. 静态代码分析器是愚蠢的。 I think that for senior developers, they are more a cause of concern than being an aide. 我认为对于高级开发人员来说,他们更多的是引起关注而不是助手。 False flags raised by these analyzers for suspected serialization/passivation problems is certainly of very limited value because CDI requires the container to validate that the instance "truly is passivation capable and that, in addition, its dependencies are passivation capable" or otherwise "throw a subclass of javax.enterprise.inject.spi.DeploymentException" (section "6.6.5. Validation of passivation capable beans and dependencies" and "2.9. Problems detected automatically by the container"). 这些分析器针对可疑的序列化/钝化问题引发的虚假标志肯定是非常有限的价值,因为CDI要求容器验证实例“真正具有钝化能力,此外,它的依赖性是能够钝化的”或者“抛出” javax.enterprise.inject.spi.DeploymentException的子类“(”6.6.5。验证具有钝化能力的bean和依赖关系“和”2.9。容器自动检测到的问题“)。

Finally, as others have pointed out, it is worth repeating: we should probably never mark a field as transient . 最后,正如其他人所指出的那样,值得重复一遍:我们应该永远不要将一个领域标记为transient

PMD and FindBugs are only checking the interfaces and also have no information about the environment in which your code will be running. PMD和FindBugs只检查接口,也没有关于代码运行环境的信息。 To quiet the tools, you could mark them as transient, but they'll all be properly re-injected upon deserialization and first use regardless of the transient keyword. 要使工具安静,可以将它们标记为瞬态,但是在反序列化时它们都将被正确地重新注入,并且无论transient关键字如何都可以首次使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM