#java #serialization #tomcat #intellij-idea #spring-webflow
#java #сериализация #tomcat #intellij-idea #spring-webflow
Вопрос:
У меня очень странная проблема с приложением Spring 3.5, Webflow 2.2, запущенным в Tomcat (6.0.32)
Часть вызовов webflow для извлечения списка объектов. Этот список помещается в объект viewScope для использования в представлении JSP. Каждый объект в этом списке наследуется от базового класса, который содержит некоторые общие поля.
Затем webflow перенаправляет в представление JSP, которое отображает содержимое этого списка путем записи всех объектов.
Когда я запускаю это из автономного Tomcat, поля в базовом классе каким-то образом имеют значение null. Я отладил и подтвердил, что список построен правильно из кода, вызванного webflow. Итак, где-то между помещением списка в область просмотра webflow и последующим его извлечением JSP поля базового класса сбрасываются в null.
ДЕЙСТВИТЕЛЬНО странная часть проблемы заключается в том, что приложение работает идеально, если я запускаю Tomcat из Intellij IDEA. Я пробовал различные версии JDK и Tomcat, и у всех одна и та же проблема.
Похоже, что это проблема webflow (возможно, неправильная сериализация?), которая зависит от того, был ли Tomcat запущен из IntelliJ.
Что делает IntelliJ, что может привести к неправильной работе сериализации Java?
Комментарии:
1. Также — если бы какая-нибудь добрая душа могла указать мне на классы в источниках Spring Webflow, которые имеют дело с сериализацией / десерализацией объектов в области просмотра и вне ее, это было бы действительно полезно. Спасибо.
2. Похоже, это ошибка сериализации. Объект Spring SerializedFlowExectionSnapshot содержит массив байтов, содержащий flowExecutionData — сериализованную форму всего, что находится в области просмотра / потока. Когда ошибка очевидна, этот массив меньше, чем когда ошибка не отображается. Некоторые данные отсутствуют!
Ответ №1:
Что ж, я не нашел точной причины, лежащей в основе, но у меня есть обходной путь. Я просто внедрил пользовательские методы сериализации объектов — private void writeObject () и readObject () — для объектов, которые таинственным образом не сериализовались должным образом, и все начало работать. В базовом классе есть только пара простых типов (пара длинных символов и строка), так что в этом нет ничего сложного.
Просто для пояснения (на случай, если кому-то интересно) — у меня был объект данных, используемый формой в моем веб-приложении. Этот объект содержит карту объектов атрибутов, где каждый объект атрибутов основан на иерархии классов примерно так:
Класс атрибута -> общий базовый класс -> корневой базовый класс
Все эти данные сохраняются в массив байтов веб-потоком Spring, а затем удаляются, когда веб-поток вызывает представление. В моем случае поля корневого базового класса не сохранялись механизмом по умолчанию.
Хотя я до сих пор точно не знаю, почему. И я до сих пор не знаю, почему, когда я запускал Tomcat из IntelliJ, сериализация РАБОТАЛА так, как ожидалось. Очень странно.
ОБНОВЛЕНИЕ: найден виновник. Действительно плохой скрипт сборки Ant, скрывающий отсутствующий маркер интерфейса «serializable».
Рассматриваемая система на самом деле довольно большая и имеет неприятный, трудноисполнимый сценарий сборки Ant. Я предполагал, что он выполняет полную, правильную чистую сборку. В иерархии классов, которую я показал выше, класс атрибутов и корневой базовый класс находились в разных jar. И jar, содержащий корневой базовый класс, фактически был кэширован и не перестроен должным образом.
Более старая (неправильная) версия развертываемого базового класса не имела «сериализуемого» интерфейса маркера (у класса атрибутов был). Поэтому, конечно, его поля не были сериализованы. Когда я тестировал из IntelliJ, он выполнял сборку и развертывание непосредственно из моих исходных текстов.
Теперь я чувствую себя действительно глупо.
Ответ №2:
Я видел подобные явления с различными IDE, когда я создавал WAR с помощью IDE, и при запуске в IDE использовались другие кэшированные классы, чем при запуске вне IDE. Чтобы сузить круг явлений, попробуйте следующее:
Если вы еще этого не сделали, создайте сборку Ant или Maven для своего проекта. Очистите все файлы .class и другие артефакты, выключите intellij, создайте WAR с помощью Ant или Maven и попробуйте развернуть это в Tomcat. Посмотрите, получаете ли вы странное поведение.
Затем попробуйте запустить сервер Tomcat в Intellij, но не развертывайте на нем свое приложение из Intellij. Разверните на нем свое приложение извне, поместив war в экземпляр Intellij Tomcat в файловой системе.
Наконец, не запускайте Tomcat из Intellij, а запустите его извне и подключитесь к нему с помощью удаленной отладки, а затем посмотрите, возникает ли проблема. Смотрите следующее о том, как настроить удаленную отладку:http://wiki.apache.org/tomcat/FAQ/Developing.
Комментарии:
1. Спасибо за комментарии, но просто чтобы было понятно, в чем проблема — IntelliJ, похоже, волшебным образом «исправляет» ошибку. Если я использую Ant-скрипт для выполнения чистой сборки и развертывания на автономном Tomcat, я получаю ошибку (и могу видеть, как она проявляется, если я подключаюсь к сеансу удаленной отладки). Если я запускаю Tomcat из IntelliJ, ошибка не возникает. Насколько я могу видеть, IntelliJ порождает новый CMD.EXE с чего запускать Tomcat, и единственное отличие во время выполнения — это Groovy jar, прикрепленный в качестве агента для выполнения горячих повторных развертываний. Я сбит с толку тем, как это может повлиять на сериализацию классов.
2. Это была не IDE, которая неправильно кэшировала классы, а неприятная сборка Ant. В остальном вы были практически правы…