ClassNotFoundException во время десериализации только что сериализованного класса

У меня возникла проблема с десериализацией объекта. Текущий проект имеет архитектуру в стиле плагинов, поэтому у меня есть jar-файлы, содержащие файлы классов, которые загружаются во время выполнения. Мне не удалось десериализовать объект, содержащий класс, найденный в одном из этих jar-файлов, поэтому я написал метод быстрого тестирования, который вызывается в середине потока, который только что загрузил плагины, создает экземпляр правильного (объект реализует определенный интерфейс поэтому я могу идентифицировать его с помощью .isAssignableFrom(..) ), сериализовать его (что происходит нормально), а затем сразу же пытается его десериализовать.

Я все еще получаю «ClassNotFoundException».

Трассировки стека:

Jul 21, 2014 4:02:11 PM com.newspinrobotics.auth.MainFrame loadPlugins
SEVERE: null
java.lang.ClassNotFoundException: com.newspinrobotics.auth.plugin.tcpserver.TCPServer
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:270)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:625)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.newspinrobotics.auth.MainFrame.loadPlugins(MainFrame.java:79)
at com.newspinrobotics.auth.MainFrame.<init>(MainFrame.java:43)
at com.newspinrobotics.auth.MainFrame$6.run(MainFrame.java:539)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

Теперь, прежде чем вы спросите. В классе TCPServer нет несериализуемых полей. В нем есть строки и примитивы. Есть одно поле, которое не попадает в эти категории, но оно помечено как временное. Существует даже конструктор без аргументов (хотя он не нужен, верно?). И да, он реализует Serializable (сериализация проходит нормально).

Я сбит с толку, как загрузчик классов может НЕ иметь его, поскольку он создает экземпляр объекта всего за несколько строк до его десериализации.

Я использую созданный на заказ ClassLoader (расширяет URLClassLoader) для загрузки файлов jar во время выполнения.

Я предоставляю публичный статический окончательный long serialVersionUID = XXXXXXXL; поле.

ИЗМЕНИТЬ:

Загрузчик классов, о котором я говорю, расширяет URLClassLoader. Он расположен внутри служебного класса, написанного другим человеком, который при дальнейшем рассмотрении на самом деле даже не является модификацией (по какой-то причине ему захотелось расширить его и не делать с ним ничего существенного). Все, что делает утилита, это выбирает файлы jar и использует URLClassLoader для добавления файлов jar в URLClassLoader через addURL(..), а также загружает классы через loadClass(..). Так что не похоже, что в этой утилите есть что-то гнусное. Однако я не ниндзя ClassLoader, поэтому я, безусловно, могу дать дополнительную информацию об этом, если это необходимо. На самом деле это всего лишь несколько служебных функций для загрузки файлов Jar, выбора файлов классов и их загрузки.

Помоги мне StackOverflow, ты моя единственная надежда (возможно).


person Aaron Marcus    schedule 21.07.2014    source источник
comment
Итак, вы реализовали пользовательский загрузчик классов, у вас возникли проблемы с загрузкой классов, и вы не публикуете какой-либо код загрузки классов (ни пользовательский загрузчик классов, ни код, который его использует, включая код, который создает объект TCPServer в первую очередь)...   -  person Tim    schedule 22.07.2014
comment
Что ж. Учитывая, что ClassLoader, кажется, работает для создания экземпляров объектов (таким образом, что они полностью пригодны для использования), похоже, проблема не в загрузчике классов.   -  person Aaron Marcus    schedule 22.07.2014
comment
Ваш пользовательский загрузчик классов не отображается в трассировке стека, поэтому он выглядит так, как будто он не используется при десериализации. Поэтому, хотя это может быть хорошо, код, который пытается его использовать (через ObjectInputStream), не выглядит таковым. Можете ли вы опубликовать соответствующий код?   -  person Tim    schedule 22.07.2014
comment
Что вы считаете актуальным? Вся утилита загрузчика классов? или определение «модифицированного загрузчика классов», которое, в конце концов, вообще не изменяется. Он расширен, но переопределяет только addURL и просто вызывает super. Так что совсем не расширен.   -  person Aaron Marcus    schedule 22.07.2014
comment
Я бы меньше беспокоился о самом ClassLoader (потому что, как вы говорите, он работает в другом месте) и больше о коде, в котором вы его вызываете. Поэтому, если наше обсуждение расширения ObjectInputStream в комментариях к моему ответу не приносит результатов, я предлагаю вам опубликовать код, в котором вы успешно создаете экземпляр TCPServer из своего загрузчика классов, а также код, в котором вы его десериализуете. безуспешно из ObjectInputStream (включая код, в котором вы создаете и настраиваете этот ObjectInputStream). Но, надеюсь, расширение ObjectInputStream будет всем, что вам нужно.   -  person Tim    schedule 22.07.2014


Ответы (1)


Вы создали подкласс ObjectInputStream, чтобы переопределить resolveClass() и использовать свой собственный Classloader? (Пример того, как это делает кто-то другой, хотя и не является прямым ответом на ваш вопрос, см. в разделе Проблема десериализации пользовательского загрузчика классов ObjectInputStream: метод resolveClass() не вызывается.)

person Tim    schedule 21.07.2014
comment
Нет, я не создавал подкласс ObjectInputStream, я просто использовал значение по умолчанию. Я вообще не думал об этом взаимодействии. - person Aaron Marcus; 22.07.2014
comment
Я не делал этого сам, но небольшое количество гуглов, которые я сделал, похоже, указывает на то, что это то, что чаще всего делают. (Могут быть другие способы сделать это, я не уверен.) Я предполагаю, что стандартный ObjectInputStream просто вызывает стандартный загрузчик классов, не давая вам возможности внедрить свой собственный загрузчик классов в середине вызова. Я немного удивлен, что нет API для передачи набора пользовательских ClassLoaders в стандартный ObjectInputStream, но, возможно, это появится в будущем. - person Tim; 22.07.2014
comment
Прости. Забыл нажать. Да, это была проблема с загрузчиком классов. Чтобы исправить это, потребовалось создать собственный ObjectInputStream. - person Aaron Marcus; 30.07.2014
comment
Привет, @AaronMarcus, как и где вы реализовали этот пользовательский ObjectInputStream? Здесь мы сталкиваемся с точно такой же проблемой: stackoverflow.com/questions/66663334/ - person Mario Marinato; 17.03.2021