Подтвердить что ты не робот

Лучшая практика для сериализации для EJB и CDI beans

Я еще не сталкивался с проблемами, связанными с сериализацией. Но PMD и Findbugs обнаруживают кучу потенциальных проблем, связанных с сериацией. Типичным случаем является инжектируемый регистратор, который обнаруживается как несериализуемый. но есть еще много - EntityManager и несколько CDI beans.

Я не нашел рекомендаций по правильной работе с сериализацией.

  • будут ли повторно введены поля, введенные @Inject и @PersistenceContext при десериализации?
  • должны ли они быть отмечены как transient?
  • или я должен просто игнорировать/отключать проверку кода?
  • Должен ли я действительно предоставлять аксессуар ко всем этим полям, как советует PMD?
4b9b3361

Ответ 1

В этом ответе будет подробно описана семантика сериализации/пассивации для 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) не имеет ничего для скажем, что представляет интерес для нас в отношении сериализации/пассивации.

Я также коснусь темы статических анализаторов кода.

EJB

Соответствующими разделами являются "4.2 Разговорный статус сеанса с состоянием Bean" и "4.2.1 Пассивирование и разговорное состояние".

@Stateless и @Singleton экземпляры никогда не пассивируются.

@Stateful экземпляры могут быть пассивированы. Начиная с EJB 3.2 разработчик класса может отказаться от пассивации с помощью @Stateful(passivationCapable=false).

Спецификация EJB явно отмечает, что ссылки на такие вещи, как UserTransaction, EntityManagerFactory и управляемые контейнером EntityManager, обрабатываются контейнером. Экземпляр @Stateful, который использует расширенный контекст персистентности, не будет пассивирован, если все объекты в контексте персистентности и реализация EntityManager не будут сериализованы.

Обратите внимание, что EntityManager, управляемый приложениями, всегда использует расширенный контекст сохранения. Кроме того, экземпляр @Stateful является единственным типом экземпляра сеанса EJB, который может использовать управляемый контейнером EntityManager с расширенным контекстом постоянства. Этот контекст сохранения будет привязан к жизненному циклу экземпляра @Stateful вместо одной транзакции JTA.

В спецификации EJB явно не указано, что происходит с управляемым контейнером EntityManager с расширенным контекстом сохранения. Мое понимание таково: если существует расширенный контекст постоянства, то этот парень должен считаться сериализуемым или нет в соответствии с правилами, определенными ранее, и если это так, то продолжается пассивация. Если пассивация продолжается, то разработчику класса @Stateful нужно только заботиться о ссылках на управляемые сущностью приложения.

Спецификация EJB не указывает, что происходит с переходными полями, кроме описания предположения, которое мы, как разработчики должны сделать.

В разделе 4.2.1 говорится:

Поставщик Bean должен предположить, что содержимое переходных полей может быть потеряно между уведомлениями PrePassivate и PostActivate.

[...]

В то время как контейнеру не требуется использовать протокол Serialization для языка программирования Java для хранения состояния пассивированного экземпляра сеанса, он должен достичь эквивалентного результата. Единственное исключение состоит в том, что контейнерам не требуется reset значение переходных полей во время активации. Объявление полей сеанса Bean как переходного является, в общем, обескураженным.

Требование, чтобы контейнер "добивался эквивалентного результата", поскольку протокол сериализации Javas в то же время оставил его полностью неуказанным относительно того, что происходит с переходными полями, является довольно грустным, если честно. Урок на дому заключается в том, что ничто не должно быть отмечено кратковременным. Для полей, которые контейнер не может обрабатывать, используйте @PrePassivate для записи null и @PostActivate для восстановления.

JPA

Слово "пассивация" не встречается в спецификации JPA. Также JPA не определяет семантику сериализации для таких типов, как EntityManagerFactory, EntityManager, Query и Parameter. Единственное предложение в спецификациях, относящихся к нам, - это (раздел "6.9 Выполнение запросов" ):

КритерииQuery, CriteriaUpdate и CriteriaУдаленные объекты должны быть сериализованы.

CDI

Раздел "6.6.4. Пассивные области" определяют пассивирующую область как область, явно аннотированную @NormalScope(passivating=true). По умолчанию это свойство имеет значение false.

Одним из следствий является то, что @Dependent - это псевдообласть - не является областью, способной к пассивации. Также следует отметить, что javax.faces.view.ViewScoped не обладает способностью к пассивации, которая по какой-либо причине кажется большей частью Интернет. Например, раздел "17-2. Разработка приложения JSF" в книге "Рецепты Java 9: ​​подход к решению проблем".

Пассивная область с пассивацией требует, чтобы экземпляры классов, объявленные "с областью действия, обладают пассивацией" (раздел "6.6.4. Пассивирующие области" ). Раздел "6.6.1. Пассивация beans" определяет такой экземпляр объекта, как один, который может быть передан вторичному хранилищу. Специальные аннотации классов или интерфейсы не являются явным требованием.

Экземпляры EJB: s @Stateless и @Singleton не являются "способными к пассивации beans". @Stateful может быть (stateful - единственный тип сеанса EJB, который имеет смысл разрешить CDI управлять жизненным циклом - т.е. Никогда не помещать область CDI на @Stateless или @Singleton). Другие "управляемые beans" - это только "пассивация, способная beans", если они и их перехватчики и декораторы являются сериализуемыми.

Не определяется как "способ, способный к пассивации Bean", не означает, что такие вещи, как stateless, singleton, EntityManagerFactory, EntityManager, Event и BeanManager, нельзя использовать в качестве зависимостей внутри экземпляра, способного к пассивации, который вы автор. Эти вещи вместо этого определяются как "зависимые от пассивации зависимости" (см. Раздел "6.6.3. Зависимые от пассивации" и "3.8. Дополнительный встроенный beans" ).

CDI делает эту децентрацию пассивацией способной с помощью прокси-сервера, способного к пассивации (см. последний маркированный элемент в разделе "5.4. Прокси клиентов" и раздел "7.3.6. Жизненный цикл ресурсов" ). Обратите внимание, что для ресурсов Java EE, таких как EntityManagerFactory и EntityManager, которые могут быть пассивацией, они должны быть объявлены как поле производителя CDI (раздел "3.7.1. Объявление ресурса" ), они не поддерживают никакой другой области, кроме @Dependent (см. раздел "3.7. Ресурсы" ), и их нужно искать на стороне клиента с помощью @Inject.

Другие экземпляры @Dependent, хотя и не объявленные с нормальной областью действия и не требуемые для выполнения клиентским прокси-сервером CDI, также могут использоваться как зависимая от пассивации, если экземпляр можно передать в вторичное хранилище, то есть сериализуемое, Этот парень будет сериализован вместе с клиентом (см. Последний маркированный элемент в разделе "5.4. Прокси клиентов" ).

Совершенно ясно и привести несколько примеров; экземпляр @Stateless, ссылка на EntityManager, созданный CDI, и сериализуемый экземпляр @Dependent, могут быть использованы в качестве полей экземпляра внутри вашего класса, аннотированных с помощью области с пассивацией.

Анализаторы статического кода

Анализаторы статического кода глупы. Я думаю, что для старших разработчиков они скорее являются причиной беспокойства, чем помощником. Ложные флаги, создаваемые этими анализаторами для подозрительных проблем с сериализацией/пассивацией, безусловно, имеют очень ограниченное значение, поскольку CDI требует, чтобы контейнер проверял, что экземпляр "действительно обладает пассивацией и что, кроме того, его зависимости являются способными к пассивации" или иначе "бросают" подкласс javax.enterprise.inject.spi.DeploymentException "(раздел 6.6.5. Проверка возможности пассивации Beans и зависимостей" и "2.9. Проблемы, обнаруженные автоматически контейнером" ).

Наконец, как указывали другие, стоит повторить: мы, вероятно, никогда не должны отмечать поле как transient.

Ответ 2

Я понимаю, что это старый вопрос, но я считаю, что единственный ответ был неверным.

будут поля, введенные @Inject и @PersistenceContext, будут rejecteded при десериализации?

Нет, они не будут. Я лично испытал это с JBoss в кластерной среде. Если bean обладает способностью к пассивации, тогда контейнер должен вводить сериализуемый прокси. Этот прокси получает сериализацию и десериализацию. После десериализации он найдет правильную инъекцию и переустановит ее. Однако, если вы отметите переходный период поля, прокси не сериализуется, и вы увидите NPE, когда доступ к вложенному ресурсу будет доступен.

Следует отметить, что внедренный ресурс или bean не обязательно должен быть Serializable, поскольку прокси-сервер будет. Единственное исключение - для @Dependent scoped beans, который должен быть сериализуемым или переходным процессом инъекции. Это связано с тем, что прокси не используется в этом случае.

если они будут отмечены как переходные?

Нет, см. выше.

или я должен просто игнорировать/отключать проверку кода?

Это зависит от вас, но это то, что я бы сделал.

Должен ли я действительно предоставлять аксессуар ко всем этим полям, как советует PMD?

Нет, я бы не стал. В наших проектах мы отключили эту проверку, когда знаем, что мы используем CDI.

Ответ 3

PMD и FindBugs проверяют только интерфейсы, а также не имеют информации о среде, в которой будет работать ваш код. Чтобы успокоить инструменты, вы можете отметить их как временные, но все они будут правильно повторно введены после десериализации и первого использования независимо от ключевого слова transient.