我还没有遇到任何与序列化相关的问题。但是PMD和Findbugs发现了一系列关于序列化的潜在问题。典型情况是注入的记录器被检测为不可序列化。但还有更多 - EntityManager
和几个CDI bean。
我没有找到任何关于如何正确处理序列化的最佳实践。
@Inject
和@PersistenceContext
注入的字段重新注入反序列化? transient
? 答案 0 :(得分:25)
我意识到这是一个老问题,但我相信提供的唯一答案是不正确的。
不,他们不会。我个人在集群环境中使用JBoss体验过这一点。如果bean具有钝化能力,那么容器必须注入可序列化的代理。该代理被序列化和反序列化。一旦反序列化,它将找到正确的注入并重新连接它。但是,如果您将字段标记为瞬态,则代理不会序列化,您将在访问注入的资源时看到NPE。将由@Inject和@PersistenceContext注入的字段为 重新注入反序列化?
应该注意,注入的资源或bean不必是Serializable,因为代理将是。唯一的例外是@Dependent范围的bean,它们必须是可序列化的或者是注入瞬态的。这是因为在这种情况下不使用代理。
它们应该标记为瞬态吗?
不,见上文。
或者我应该忽略/关闭代码检查?
这取决于你,但这就是我要做的。
我应该像PMD建议的那样真正提供所有这些字段的访问者吗?
不,我不会。 在我们的项目中,当我们知道我们正在使用CDI时,我们会禁用此检查。
答案 1 :(得分:7)
这个答案将详细介绍EJB 3.2(JSR 345),JPA 2.1(JSR 338)和CDI 1.2(JSR 346)的序列化/钝化语义。值得注意的是,Java EE 7伞规范(JSR 342),Managed Beans 1.0规范(JSR 316)和Commons Annotations规范1.2(JSR 250)没有任何可说的说法。我们感兴趣的是序列化/钝化。
我还将讨论静态代码分析器的主题。
相关部分是" 4.2有状态会话Bean的会话状态"和" 4.2.1实例钝化和会话状态"。
@Stateless
和@Singleton
个实例永远不会被钝化。
@Stateful
个实例可能会被钝化。从EJB 3.2开始,类开发人员可以使用@Stateful(passivationCapable=false)
选择退出钝化。
EJB规范明确指出,容器会处理对UserTransaction
,EntityManagerFactory
和容器管理EntityManager
等内容的引用。除非持久化上下文和EntityManager实现中的所有实体都是可序列化的,否则不会钝化使用扩展持久性上下文的@Stateful实例。
请注意,应用程序管理的EntityManager始终使用扩展的持久性上下文。此外,@ Stateful实例是EJB会话实例的唯一类型,它可以使用容器管理的EntityManager和扩展的持久化上下文。此持久性上下文将绑定到@Stateful实例的生命周期,而不是单个JTA事务。
EJB规范没有明确解决带有扩展持久化上下文的容器管理的EntityManager会发生什么。我的理解是这样的:如果有一个扩展的持久化上下文,那么根据之前定义的规则,这个人必须被认为是可序列化的,如果是,则进行钝化。如果钝化继续进行,那么@Stateful类开发人员只需关注自己对应用程序管理的实体管理器的引用。
EJB规范没有指定瞬态字段会发生什么,除了描述我们作为开发人员应该做出的假设。
第4.2.1节说:
Bean Provider必须假设在PrePassivate和PostActivate通知之间可能会丢失瞬态字段的内容。
[...]
虽然容器不需要使用Java编程语言的Serialization协议来存储钝化会话实例的状态,但它必须达到相同的结果。一个例外是容器在激活期间不需要重置瞬态字段的值。一般来说,不鼓励将会话bean的字段声明为瞬态。
要求容器达到相同的效果"正如Javas序列化协议同时完全没有说明瞬态字段发生的事情是非常可悲的。带回家的教训是,任何东西都不应该被标记为短暂的。对于容器无法处理的字段,请使用@PrePassivate
编写null
和@PostActivate
进行还原。
"钝化"在JPA规范中没有出现。 JPA也没有为EntityManagerFactory
,EntityManager
,Query
和Parameter
等类型定义序列化语义。规范中与我们相关的唯一一句话就是这一点(#34; 6.9查询执行"):
CriteriaQuery,CriteriaUpdate和CriteriaDelete对象必须是可序列化的。
第" 6.6.4节。钝化范围"将钝化范围定义为显式注释@NormalScope(passivating=true)
的范围。此属性默认为false。
一个含义是@Dependent
- 这是一个伪范围 - 不是一个具有钝化能力的范围。同样值得注意的是,javax.faces.view.ViewScoped
不是一个具有钝化能力的范围,无论出于什么原因,大多数互联网似乎都相信这一范围。例如,部分" 17-2。开发JSF应用程序"在书中" Java 9食谱:问题解决方法"。
具有钝化功能的范围要求声明"类的实例具有钝化能力" (第" 6.6.4。钝化范围")。第" 6.6.1节。具有钝化功能的豆类#34;将这样的对象实例简单地定义为可转移到辅助存储的对象实例。特殊的类 - 注释或接口不是明确的要求。
EJB的实例:@Stateless和@Singleton不是"具有钝化功能的bean"。 @Stateful可能是(有状态是唯一允许CDI管理生命周期的EJB会话类型 - 即,永远不会将CDI范围放在@Stateless或@Singleton上)。其他"托管豆"只有"钝化能力的豆类"如果他们和他们的拦截器和装饰器都是可序列化的。
未定义为具有钝化能力的豆类#34;并不意味着诸如无状态,单例,EntityManagerFactory,EntityManager,Event和BeanManager之类的东西不能用作您创作的具有钝化功能的实例中的依赖项。相反,这些东西被定义为"具有钝化能力的依赖性" (参见" 6.6.3。具有钝化能力的依赖关系"和" 3.8。其他内置bean")。
CDI通过使用具有钝化功能的代理来使这些依赖性能够被钝化(参见第34节中的最后一个项目符号项目; 5.4。客户端代理"以及#34; 7.3.6。资源的生命周期" )。请注意,对于要使其具有钝化功能的Java EE资源(例如EntityManagerFactory和EntityManager),必须将它们声明为CDI生产者字段(第34节“3.7.1。声明资源"”),它们不支持任何除了@Dependent之外的其他范围(参见" 3.7。资源"部分),必须使用@Inject在客户端查找它们。
其他@Dependent实例 - 虽然没有声明具有正常范围,并且不需要由CDI"客户端代理" - 如果实例可以转移到二级存储,即可序列化,则也可以用作具有钝化能力的依赖性。这个人将与客户一起序列化(参见第34节中的最后一个项目符号项; 5.4。客户代理和#34;)。
非常清楚并提供一些例子; @Stateless实例,对CDI生成的EntityManager的引用和可序列化的@Dependent实例都可以用作类中的实例字段,并使用具有钝化功能的范围进行注释。
静态代码分析器很愚蠢。我认为对于高级开发人员来说,他们更多的是引起关注而不是助手。这些分析器针对可疑的序列化/钝化问题引发的错误标志当然具有非常有限的价值,因为CDI要求容器验证实例是否真正具有钝化能力,并且此外,其依赖性具有钝化能力"或者以其他方式"抛出javax.enterprise.inject.spi.DeploymentException"的子类。 (第" 6.6.5。验证具有钝化功能的bean和依赖关系"和" 2.9。容器自动检测到的问题")。
最后,正如其他人所指出的那样,值得重复一遍:我们应该永远不要将字段标记为transient
。
答案 2 :(得分:1)
PMD和FindBugs仅检查接口,并且没有关于运行代码的环境的信息。要使工具安静,可以将它们标记为瞬态,但是在反序列化时它们都将被正确地重新注入,并且无论transient关键字如何都可以首次使用。