kos*_*tja 22 java serialization ejb cdi deserialization
我还没有遇到任何序列化相关的问题.但是PMD和Findbugs发现了一系列关于序列化的潜在问题.典型情况是注入的记录器被检测为不可序列化.但还有更多 - EntityManager
以及几个CDI bean.
我没有找到任何关于如何正确处理序列化的最佳实践.
@Inject
并@PersistenceContext
重新注入?transient
?小智 25
我意识到这是一个老问题,但我相信提供的唯一答案是不正确的.
将@Inject和@PersistenceContext注入的字段重新注入反序列化?
不,他们不会.我个人在集群环境中使用JBoss体验过这一点.如果bean具有钝化能力,那么容器必须注入可序列化的代理.该代理被序列化和反序列化.一旦反序列化,它将找到正确的注入并重新连接它.但是,如果将字段标记为瞬态,则代理不会序列化,并且在访问注入的资源时您将看到NPE.
应该注意,注入的资源或bean不必是Serializable,因为代理将是.唯一的例外是@Dependent范围的bean,它们必须是可序列化的或者是注入瞬态的.这是因为在这种情况下不使用代理.
它们应该标记为瞬态吗?
不,见上文.
或者我应该忽略/关闭代码检查?
这取决于你,但这就是我要做的.
PMD建议我应该真正提供所有这些领域的访问者吗?
我不会.在我们的项目中,当我们知道我们正在使用CDI时,我们会禁用此检查.
这个答案将详细介绍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编程语言的序列化协议来存储钝化的会话实例的状态,但它必须实现相同的结果.一个例外是容器在激活期间不需要重置瞬态字段的值.一般来说,不鼓励将会话bean的字段声明为瞬态.
要求容器"实现与Javas序列化协议相同的结果",同时让它完全没有说明瞬态字段会发生什么,这是非常可悲的,说实话.带回家的教训是,任何东西都不应该被标记为短暂的.对于容器无法处理的字段,请使用@PrePassivate
写入null
和@PostActivate
还原.
JPA规范中没有出现"钝化"一词.JPA也不限定系列化语义类型,例如EntityManagerFactory
,EntityManager
,Query
和Parameter
.规范中与我们相关的唯一一句是("6.9查询执行"部分):
CriteriaQuery,CriteriaUpdate和CriteriaDelete对象必须是可序列化的.
"6.6.4.钝化作用域"一节将钝化作用域定义为显式注释的作用域@NormalScope(passivating=true)
.此属性默认为false.
一个含义是@Dependent
- 这是一个伪范围 - 不是一个钝化能力的范围.同样值得注意的是,这javax.faces.view.ViewScoped
不是一个无法通过互联网似乎相信的任何原因的钝化范围.例如,"Java 9 Recipes:A Problem-Solution Approach"一书中的"17-2.开发JSF应用程序"一节.
具有钝化能力的范围要求声明"具有范围的类的实例具有钝化能力"("6.6.4.钝化范围"部分)."6.6.1.具有钝化功能的bean"一节将这样的对象实例定义为可转移到二级存储的对象实例.特殊的类注释或接口不是明确的要求.
EJB实例:@Stateless和@Singleton不是"具有钝化功能的bean".@Stateful可能是(有状态是唯一允许CDI管理生命周期的EJB会话类型 - 即,永远不会将CDI范围放在@Stateless或@Singleton上).如果它们及其拦截器和装饰器都是可序列化的,那么其他"托管bean"只是"具有钝化功能的bean".
没有被定义为"钝化能豆"并不意味着事情,如无状态的,单,的EntityManagerFactory,EntityManager的,事件和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
.
归档时间: |
|
查看次数: |
5881 次 |
最近记录: |