Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Коллоквиум 2012.doc
Скачиваний:
25
Добавлен:
25.08.2019
Размер:
346.11 Кб
Скачать

11. Сохраняемость. Serializable и Externalizable. Программирование распределенных приложений

Сохраняемость  — это способность сохранять Веаn-компонент в энергонезависимой памяти и восстанавливать его в более позднее время. Особенно важно запоминать параметры конфигурации.

Механизм, который делает сохраняемость возможной называется сериализацией.

Сущ. два способа сделать объект сериализуемым: объявление класса реализующим интерфейс java.io.Serializable и реализация интерфейса java.io.Externalizable. Эти интерфейсы предлагают выбор между автоматической сериализацией и сериализацией по усмотрению пользователя. Если какой-либо класс в иерархии наследования наследует реализации Serializable или Externalizable, этот класс является сериализуемым.

Интерфейс Serializable обеспечивает автоматическую сериализацию, используя инструменты Java Object Serialization. Serializable не объявляет методов: он действует как маркер, сообщая инструментам Object Serialization, что класс Бина является сериализуемым. Пометка класса Serializable означает, что JVM сообщают, что уверены, что класс будет работать с сериализацией по умолчанию. Несколько важных пунктов по поводу работы с интерфейсом Serializable:  - Классы, которые реализуют Serializable, должны иметь конструктор без параметров. Этот конструктор будет вызываться, когда объект "воссоздается" из файла .ser.  - Не нужно реализовывать Serializable в классе, если он уже реализован в суперклассе (но нужно убедиться, что он работает корректно и так, как ожидается, с сериализацией по умолчанию).  Все поля, кроме static и transient сериализуются. Модификатор transient  исп. для задания полей, которые не хотят сериализовать и для задания классов, которые не являются сериализуемыми.  BeanBox записывает сериализованные Бины в файл с расширением .ser. 

Стандартная сериализация, с использованием java.io.Serializable работает через Reflection API. Т.е. класс разбирается как набор полей, каждое из которых пишется в выходной поток.

Если сериализуемый класс содержит любой из следующих двух методов (сигнатуры должны быть точно такими), то сериализации по умолчанию не будет.  private void writeObject(java.io.ObjectOutputStream out) throws IOException; private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; Можно управлять тем, как сериализуются более сложные объекты, написав собственные реализации методов writeObject и readObject. writeObject реализуют, когда хотят осуществлять больший контроль над тем, как происходит сериализация, сериализовать объекты которые сериализация по умолчанию не обрабатывает, или когда нужно добавлять в поток сериализации данные, которые не являются членами объекта. readObject реализуют для реконструирования потока данных, записанного writeObject. 

В отличие от java.io.Serializable, интерфейс Externalizable содержит два метода, которые необходимо реализовать – writeExternal(ObjectOutput) и readExternal(ObjectInput). В этих методах как раз и находится логика сериализации/десериализации.

При использовании Serializable десериализация происходит так: под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается. При использовании же Externalizable ситуация иная. Сначала вызывается конструктор без параметров, а потом уже на созданном объекте вызывается метод readExternal, который и вычитывает, собственно, все свои данные. Потому – любой реализующий интерфейс Externalizable класс обязан иметь public конструктор без параметров. Более того, поскольку все наследники такого класса тоже будут считаться реализующими интерфейс Externalizable, у них тоже должен быть конструктор без параметров.

Поля transient действует только на стандартный механизм сериализации. При использовании Externalizable никто не мешает сериализовать это поле, равно как и вычитать его. Если поле объявлено transient, то при десериализации объекта оно принимает значение по умолчанию.

Поля с модификатором final сериализуются как и обычные. За одним исключением – их невозможно десериализовать при использовании Externalizable. Т.к. final-поля должны быть инициализированы в конструкторе, а после этого в readExternal изменить значение этого поля будет невозможно. Если необходимо сериализовать объект, имеющий final-поле, вам придется использовать только стандартную сериализацию.

При стандартной сериализации учитывается порядок объявления полей в классе. Потому, при изменении порядка десериализация пройдет не так, как надо. Чтобы этого избежать, есть следующий механизм. В каждый класс, реализующий интерфейс Serializable, на стадии компиляции добавляется еще одно поле – private static final long serialVersionUID. Это поле содержит уникальный идентификатор версии сериализованного класса. Оно вычисляется по содержимому класса – полям, их порядку объявления, методам, их порядку объявления. При любом изменении в классе это поле поменяет свое значение. Это поле записывается в поток при сериализации класса. При десериализации значение этого поля сравнивается с имеющимся у класса в виртуальной машине. Если значения не совпадают – инициируется исключение.

Ссылки на объекты после десериализации отличаются от ссылок до нее. Иначе говоря, при сериализации/десериализации объект был скопирован. Этот метод используется иногда для клонирования объектов. При сериализации/десериализации нескольких объектов, имеющих перекрестные ссылки, эти ссылки остаются действительными после десериализации. Иначе говоря, если до сериализации они указывали на один объект, то после десериализации они тоже будут указывать на один объект.

Если одновременно реализовать у класса Externalizable и Serializable, то будет исп. Externalizable. Механизм сериализации сначала проверяет его наличие, а уж потом – наличие Serializable  При наследовании от класса, реализующего Serializable, никаких дополнительных действий предпринимать не надо. Сериализация будет распространяться и на дочерний класс. При наследовании от класса, реализующего Externalizable, необходимо переопределить методы родительского класса readExternal и writeExternal. Иначе поля дочернего класса сериализованы не будут. В этом случае надо бы не забыть вызвать родительские методы, иначе не сериализованы будут уже родительские поля.

Расширенная сериализация дает гораздо большую гибкость, зачастую она может дать немалый выигрыш по объему сериализованных данных и выше по производительности. Однако, поскольку протокол сериализации не используется – контроль целостности остается на самом разработчике.

RMI (Remote Method Invocation) — программный интерфейс вызова удаленных методов в языке Java.  Это распределенная объектная модель, специфицирующая, каким образом производится вызов удаленных методов, работающих на другой виртуальной машине Java.

При доступе к объектам на другом компьютере возможно вызывать методы этого объекта. Необходимо только доставить параметры метода на другой компьютер, сообщить объекту о необходимости выполнения метода, а затем передать обратно возвращаемое значение. Механизм RMI дает возможность организовать выполнение всех этих операций.

В терминах RMI объект, который вызывает удаленный метод, называется клиентским объектом, а удаленный объект — серверным объектом. Компьютеры выступают в роли клиента и сервера только для конкретного вызова. Вполне возможно, что при выполнении следующей операции эти компьютеры поменяются ролями, то есть сервер предыдущего вызова может сам стать клиентом при обращении к объекту на другом компьютере.

При вызове метода удаленного объекта на самом деле вызывается обычный метод языка Java, инкапсулированный в специальном объекте-заглушке (stub), который является представителем серверного объекта. Заглушка находится на клиентском компьютере, а не на сервере. Она упаковывает параметры удаленного метода в блок байтов. Каждый параметр кодируется с помощью алгоритма, обеспечивающего независимость от аппаратуры. Например, числа всегда передаются в порядке, при котором сначала передается старший байт (big-endian). При этом объекты подвергаются сериализации. Процесс кодирования параметров называется развертыванием параметров (parameter marshaling). Основная цель развертывания параметров — преобразование их в формат, пригодный для передачи параметров от одной виртуальной машины к другой.

Метод, принадлежащий заглушке, создает блок, в который входят следующие элементы: - идентификатор удаленного объекта; - описание вызываемого метода; - развернутые параметры.

Затем метод заглушки посылает эту информацию серверу. Далее объект-получатель выполняет для каждого вызова удаленного метода следующие действия: - свертывание параметров; - поиск вызванного объекта; - вызов заданного метода; - извлечение и развертывание возвращаемого значения или исключения, сгенерированного данным методом; - передача пакета, состоящего из развернутых возвращаемых данных, объекту-заглушке на клиентском компьютере.

Клиентский объект-заглушка свертывает возвращаемое значение или исключение, полученное с сервера. Результат свертывания становится возвращаемым значением метода заглушки. Если удаленный метод возвращает исключение, то объект-заглушка повторит его в среде объекта-клиента.

Для доступа к удаленным методам клиентский код всегда использует объектные переменные типы interface.

Конечно, интерфейсы представляют собой абстракции и содержат только перечень методов. Переменные типа interface всегда должны быть связаны с фактическим объектом. При вызове удаленных объектов переменная ссылается на объект-заглушку. При этом клиентская программа ничего не знает о типе заглушки, а сами заглушки и связанные с ними объекты создаются автоматически.

При передаче объекта другой программе (он может быть параметром либо возвращаемым значением удаленного метода) нужен файл класса, соответствующий этому объекту. Например, метод, который возвращает значение типа Product. При компиляции клиентской программы должен быть сгенерирован файл класса Product.class.

При загрузке фрагментов кода по сети всегда возникают сомнения по поводу должного обеспечения безопасности. В связи с этим в приложениях с использованием RMI применяется диспетчер защиты. Он защищает заглушки от проникновения в них вирусов.