Best practice for serialization for EJB and CDI beans

后端 未结 3 829
面向向阳花
面向向阳花 2021-02-01 18:03

I have not yet experienced any serialization-related issues. But PMD and Findbugs detect a bunch of potential problems regarding seriazation. A typical case is an injected logge

相关标签:
3条回答
  • 2021-02-01 18:38

    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). 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.

    I will also touch on the topic of static code analyzers.

    EJB

    Relevant sections are "4.2 Conversational State of a Stateful Session Bean" and "4.2.1 Instance Passivation and Conversational State".

    @Stateless and @Singleton instances are never passivated.

    @Stateful instances may be passivated. Since EJB 3.2, the class developer can opt-out from passivation using @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. 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.

    Please note that an application-managed EntityManager always uses an extended persistence context. Also, a @Stateful instance is the only type of EJB session instance which may use a container-managed EntityManager with an extended persistence context. This persistence context would be bound to the life cycle of the @Stateful instance instead of one single JTA transaction.

    The EJB specification does not explicitly address what happens to a container-managed EntityManager with an extended persistence context. 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.

    The EJB specification does not specify what happens to transient fields other than describing an assumption we as developers should make.

    Section 4.2.1 says:

    The Bean Provider must assume that the content of transient fields may be lost between the PrePassivate and PostActivate notifications.

    [...]

    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. 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.

    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. 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.

    JPA

    The word "passivation" does not occur in the JPA specification. Nor does JPA define serialization semantics for types such as EntityManagerFactory, EntityManager, Query and Parameter. The only sentence in the specification relevant to us is this (section "6.9 Query Execution"):

    CriteriaQuery, CriteriaUpdate, and CriteriaDelete objects must be serializable.

    CDI

    Section "6.6.4. Passivating scopes" define a passivating scope as a scope explicitly annotated @NormalScope(passivating=true). This property defaults to false.

    One implication is that @Dependent - which is a pseudo scope - is not a passivation capable scope. 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. For example, section "17-2. Developing a JSF Application" in the book "Java 9 Recipes: A Problem-Solution Approach".

    A passivation capable scope requires that instances of classes declared "with the scope are passivation capable" (section "6.6.4. Passivating scopes"). Section "6.6.1. Passivation capable beans" define such an object instance simply as one being transferable to secondary storage. Special class- annotations or interfaces are not an explicit requirement.

    Instances of EJB:s @Stateless and @Singleton are not "passivation capable beans". @Stateful may be (stateful is the only EJB session type which it makes sense to let CDI manage the life cycle of - i.e., never put a CDI scope on a @Stateless or @Singleton). Other "managed beans" are only "passivation capable beans" if they and their interceptors and decorators are all serializable.

    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. These things are instead defined as "passivation capable dependencies" (see section "6.6.3. Passivation capable dependencies" and "3.8. Additional built-in beans").

    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"). 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.

    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, i.e., serializable. This guy will be serialized together with the client (see last bulleted item in section "5.4. Client proxies").

    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.

    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").

    Finally, as others have pointed out, it is worth repeating: we should probably never mark a field as transient.

    0 讨论(0)
  • 2021-02-01 18:46

    PMD and FindBugs are only checking the interfaces and also have no information about the environment in which your code will be running. 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.

    0 讨论(0)
  • 2021-02-01 18:58

    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?

    No, they will not. I personally experienced this with JBoss in a clustered environment. If the bean is passivation capable, then the container must inject a serializable proxy. 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.

    It should be noted that the injected resource or bean does not have to be Serializable, because the proxy will be. The only exception is for @Dependent scoped beans which have to be serializable or the injection transient. 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?

    No, I would not. In our projects, we disable this check when we know we are using CDI.

    0 讨论(0)
提交回复
热议问题