我尚未遇到任何与序列化相关的问题。但是PMD和Findbugs发现了一系列关于序列化的潜在问题。典型的情况是注入的记录器被检测为不可序列化。但是还有更多- EntityManager还有几个CDI bean。
EntityManager
我还没有找到有关如何正确处理序列化的最佳实践。
@Inject
@PersistenceContext
transient
该答案将详细描述EJB 3.2(JSR 345),JPA 2.1(JSR 338)和CDI 1.2(JSR 346)的序列化/钝化语义。值得注意的是,Java EE 7伞规范(JSR 342),托管Beans 1.0规范(JSR 316)和Commons Annotations规范1.2(JSR 250)在序列化/钝化。
我还将讨论静态代码分析器的主题。
相关章节为“ 4.2有状态会话Bean的会话状态”和“ 4.2.1实例钝化和会话状态”。
@Stateless并且@Singleton实例永远不会被钝化。
@Stateless
@Singleton
@Stateful实例可能被钝化。从EJB 3.2开始,类开发人员可以使用退出钝化@Stateful(passivationCapable=false)。
@Stateful
@Stateful(passivationCapable=false)
EJB规范明确指出来的东西,如引用UserTransaction,EntityManagerFactory和容器管理EntityManager由容器的照顾。除非持久化上下文中的所有实体以及EntityManager实现可序列化,否则使用扩展的持久化上下文的@Stateful实例将不会被钝化。
UserTransaction
EntityManagerFactory
请注意,由应用程序管理的EntityManager始终使用扩展的持久性上下文。而且,@ Stateful实例是唯一的EJB会话实例类型,可以使用具有扩展持久性上下文的容器管理的EntityManager。此持久性上下文将绑定到@Stateful实例的生命周期,而不是单个JTA事务。
EJB规范未明确解决具有扩展的持久性上下文的容器管理的EntityManager所发生的情况。我的理解是:如果存在扩展的持久性上下文,则必须根据先前定义的规则将此人视为可序列化或不可序列化;如果是,则将进行钝化。如果进行钝化,则@Stateful类开发人员只需要关心对应用程序管理的实体管理器的引用即可。
EJB规范除了描述我们作为开发人员应该做出的假设外,没有指定瞬态字段会发生什么。
第4.2.1节说:
Bean Provider必须假定在PrePassivate和PostActivate通知之间,临时字段的内容可能会丢失。 […] 虽然不需要该容器使用Java编程语言的序列化协议来存储钝化会话实例的状态,但它必须达到等效的结果。一个例外是,在激活期间不需要容器重置瞬态字段的值。通常不建议将会话bean的字段声明为瞬态。
Bean Provider必须假定在PrePassivate和PostActivate通知之间,临时字段的内容可能会丢失。
[…]
虽然不需要该容器使用Java编程语言的序列化协议来存储钝化会话实例的状态,但它必须达到等效的结果。一个例外是,在激活期间不需要容器重置瞬态字段的值。通常不建议将会话bean的字段声明为瞬态。
坦白地说,要求容器同时“实现等效结果”作为Java的序列化协议,而对于瞬态字段的发生情况却完全不确定,这是非常可悲的。值得一提的是,不应将任何内容标记为瞬态。对于容器无法处理的字段,请使用@PrePassivate编写null并@PostActivate还原。
@PrePassivate
null
@PostActivate
在JPA规范中不会出现“钝化”一词。JPA也不限定系列化语义类型,例如EntityManagerFactory,EntityManager,Query和Parameter。规范中与我们有关的唯一一句话是(第6.9节“查询执行”):
Query
Parameter
CriteriaQuery,CriteriaUpdate和CriteriaDelete对象必须可序列化。
“ 6.6.4。钝化作用域”一节将钝化作用域定义为显式标注的作用域@NormalScope(passivating=true)。此属性默认为false。
@NormalScope(passivating=true)
一个暗示是@Dependent-这是一个伪作用域- 不是可钝化的作用域。同样值得注意的是,javax.faces.view.ViewScoped无论出于何种原因,大多数Internet似乎都认为这不是具有钝化能力的范围。例如,“ Java 9食谱:一种问题解决方法”一书中的“ 17-2。开发JSF应用程序”一节。
@Dependent
javax.faces.view.ViewScoped
具有钝化能力的作用域要求声明为“具有该作用域的类具有钝化能力”的类的实例(第6.6.4节“钝化作用域”)。“ 6.6.1。具有钝化能力的Bean”部分将此类对象实例定义为一个可转移到辅助存储的对象实例。特殊的类注释或接口不是明确的要求。
EJB:@Stateless和@Singleton的实例不是“具有钝化能力的bean”。@Stateful可能是(Stateful是让CDI管理生命周期的唯一有意义的EJB会话类型-即,永远不要将CDI范围放在@Stateless或@Singleton上)。如果其他“托管bean”及其拦截器和装饰器都可序列化,则它们仅是“具有钝化能力的bean”。
不被定义为“具有钝化能力的bean”并不意味着诸如无状态,单例,EntityManagerFactory,EntityManager,Event和BeanManager之类的东西不能用作您编写的具有钝化能力的实例内的依赖项。这些东西被定义为“具有钝化能力的依赖关系”(请参见“ 6.6.3。具有钝化能力的依赖关系”和“ 3.8。其他内置bean”一节)。
CDI通过使用具有钝化能力的代理来使这些伪装能力得以钝化(请参阅“ 5.4。客户代理”和“ 7.3.6。资源生命周期”中的最后一个项目符号)。请注意,要使Java EE资源(例如EntityManagerFactory和EntityManager)具有钝化能力,必须将它们声明为CDI生产者字段(“ 3.7.1。声明资源”部分),除@Dependent之外,它们不支持任何其他范围(请参阅“ 3.7。资源”一节),并且必须在客户端使用@Inject查找它们。
如果实例可以转移到辅助存储(即可序列化),则其他@Dependent实例(尽管未使用正常范围声明且不需要由CDI“客户端代理”进行前置)也可以用作具有钝化能力的依赖项。该人员将与客户一起被序列化(请参阅“ 5.4。客户代理”部分中的最后一个项目符号)。
十分清楚并提供一些示例;@Stateless实例,对由CDI生成的EntityManager的引用和可序列化的@Dependent实例,都可以用作您的类中具有可钝化作用域的注释的实例字段。
静态代码分析器很愚蠢。我认为对于高级开发人员而言,他们不是助手,而是引起关注的原因。这些分析器针对可疑的序列化/钝化问题提出的错误标志肯定具有非常有限的价值,因为CDI要求容器验证实例“确实具有钝化能力,并且其依赖项具有钝化能力”或“抛出”异常。 javax.enterprise.inject.spi.DeploymentException的子类”(“ 6.6.5。具有钝化能力的bean和依赖项的验证”和“ 2.9。容器自动检测到的问题”)。
最后,正如其他人指出的,值得重复:我们可能永远不应该将字段标记为transient。