5) Serializible, Externalizable
Сериализация в контексте протокола HTTP:
Сериализация в контексте HTTP - это процесс преобразования объекта Java в формат, который можно передать по сети с использованием протокола HTTP. Обычно объект преобразуется в JSON, XML или другой текстовый формат, который можно включить в тело HTTP-запроса или ответа.
// Используем библиотеку Jackson для сериализации в JSON
ObjectMapper objectMapper = new ObjectMapper();
User user = new User("John", "Doe");
String jsonString = objectMapper.writeValueAsString(user);
System.out.println(jsonString); // {"firstName":"John","lastName":"Doe"}
Десериализация в контексте протокола HTTP:
Десериализация - это обратный процесс сериализации. В контексте HTTP это преобразование данных, полученных в теле HTTP-запроса или ответа (например, JSON или XML), обратно в объект Java.
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\"}";
User user = objectMapper.readValue(jsonString, User.class);
System.out.println(user.getFirstName()); // John
HttpMessageConverter:
HttpMessageConverter - это интерфейс в Spring Framework, который отвечает за преобразование HTTP-запросов и ответов. Он играет ключевую роль в процессах сериализации и десериализации. Подробная информация о HttpMessageConverter:
Основная задача: преобразование объектов Java в HTTP-сообщения и обратно. Работает в обоих направлениях: от Java-объекта к HTTP-сообщению (для ответов) и от HTTP-сообщения к Java-объекту (для запросов). Spring предоставляет несколько реализаций для работы с различными форматами данных (JSON, XML, форм-данные и т.д.). Разработчики могут создавать собственные реализации для поддержки специфических форматов данных.
Spring поставляет несколько реализаций HttpMessageConverter для различных типов данных:
MappingJackson2HttpMessageConverter — для JSON.
Jaxb2RootElementHttpMessageConverter — для XML.
StringHttpMessageConverter — для строковых данных.
Примеры для лучшего понимания:
Использование Jackson для JSON:
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
public class HttpConverterExample {
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
// Добавляем Jackson конвертер в RestTemplate
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// Теперь RestTemplate может автоматически сериализовать и десериализовать JSON
MyObject myObject = restTemplate.getForObject("http://example.com/api/data", MyObject.class);
// Отправка объекта на сервер (сериализация в JSON)
restTemplate.postForObject("http://example.com/api/data", myObject, String.class);
}
}
class MyObject {
private String name;
private int age;
// геттеры и сеттеры
}
В этом примере MappingJackson2HttpMessageConverter автоматически обрабатывает сериализацию объекта MyObject в JSON при отправке POST-запроса и десериализацию JSON в MyObject при получении GET-запроса.
Создание пользовательского HttpMessageConverter:
Иногда может потребоваться создать собственный конвертер для обработки специфического формата данных.
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<MyCustomObject> {
public CustomHttpMessageConverter() {
super(new MediaType("application", "x-custom", StandardCharsets.UTF_8));
}
@Override
protected boolean supports(Class<?> clazz) {
return MyCustomObject.class.isAssignableFrom(clazz);
}
@Override
protected MyCustomObject readInternal(Class<? extends MyCustomObject> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// Реализация десериализации
String body = StreamUtils.copyToString(inputMessage.getBody(), StandardCharsets.UTF_8);
// Пример простой десериализации
String[] parts = body.split("\\|");
return new MyCustomObject(parts[0], Integer.parseInt(parts[1]));
}
@Override
protected void writeInternal(MyCustomObject myCustomObject, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// Реализация сериализации
String output = myCustomObject.getName() + "|" + myCustomObject.getAge();
outputMessage.getBody().write(output.getBytes(StandardCharsets.UTF_8));
}
}
class MyCustomObject {
private String name;
private int age;
// конструктор, геттеры и сеттеры
}
Этот пользовательский конвертер обрабатывает объекты типа MyCustomObject, сериализуя их в строку формата "name|age" и десериализуя из этого же формата. Для использования этого конвертера его нужно зарегистрировать в конфигурации Spring:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new CustomHttpMessageConverter());
}
}
Эти примеры демонстрируют, как HttpMessageConverter играет ключевую роль в процессах сериализации и десериализации в контексте HTTP-протокола, позволяя легко преобразовывать Java-объекты в формат, подходящий для передачи по сети, и обратно.
Вид сериализации:
В данном примере используется стандартная сериализация Java (Java Serialization). Этот механизм позволяет преобразовывать объекты Java в последовательность байтов, которую можно сохранить в файл или передать по сети, а затем восстановить объект из этой последовательности байтов.
Ключевые аспекты реализации:
a) Интерфейс Serializable: Класс User реализует интерфейс Serializable. Это маркерный интерфейс, который указывает JVM, что объекты этого класса могут быть сериализованы. b) serialVersionUID: Определение private static final long serialVersionUID = 1L; используется для контроля версий сериализованных объектов. Это помогает при десериализации определить, совместима ли текущая версия класса с сериализованными данными. c) Транзиентные поля: Ключевое слово transient используется для поля password. Это означает, что данное поле не будет сериализовано стандартным способом, что полезно для конфиденциальных данных. d) Пользовательская сериализация: Методы writeObject и readObject реализуют пользовательскую логику сериализации и десериализации. Это позволяет контролировать процесс и обрабатывать транзиентные поля особым образом.
Процесс сериализации и десериализации:
a) Сериализация:
Создается объект ObjectOutputStream, связанный с FileOutputStream. Метод writeObject() вызывается для записи объекта в поток. При этом автоматически вызывается пользовательский метод writeObject() класса User.
b) Десериализация:
Создается объект ObjectInputStream, связанный с FileInputStream. Метод readObject() вызывается для чтения объекта из потока. При этом автоматически вызывается пользовательский метод readObject() класса User.
Особенности и преимущества использованного подхода:
a) Безопасность: Пользовательская сериализация позволяет безопасно обрабатывать конфиденциальные данные (пароль). Несмотря на то, что поле password объявлено как transient, оно все равно сериализуется, но в зашифрованном виде. b) Гибкость: Методы writeObject и readObject позволяют реализовать любую нестандартную логику сериализации и десериализации. c) Совместимость: Использование serialVersionUID обеспечивает контроль версий, что важно при обновлении класса и работе с ранее сериализованными данными.
Смежные использования:
a) Сохранение состояния объектов: Этот механизм может использоваться для сохранения состояния приложения, например, для реализации функции "сохранить/загрузить" в играх или приложениях. b) Передача объектов по сети: Сериализация часто используется в распределенных системах для передачи объектов между разными JVM, например, в RMI (Remote Method Invocation). c) Кэширование: Сериализованные объекты могут быть сохранены в кэше для быстрого доступа без необходимости повторного создания объекта. d) Глубокое клонирование: Сериализация и последующая десериализация объекта может использоваться для создания глубокой копии объекта. Этот пример демонстрирует мощь и гибкость механизма сериализации в Java, позволяя разработчикам контролировать процесс сохранения и восстановления состояния объектов, что особенно важно при работе с конфиденциальными данными или сложными структурами объектов.
import java.io.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password;
// Конструктор, геттеры и сеттеры опущены для краткости
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject("SECRET_KEY:" + password);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
String secretPassword = (String) in.readObject();
password = secretPassword.substring(11);
}
}
public class Main {
public static void main(String[] args) {
User user = new User("John", 30, "password123");
// Сериализация
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
out.writeObject(user);
System.out.println("Объект User сериализован");
} catch (IOException e) {
e.printStackTrace();
}
// Десериализация
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.ser"))) {
User deserializedUser = (User) in.readObject();
System.out.println("Объект User десериализован");
System.out.println("Имя: " + deserializedUser.getName());
System.out.println("Возраст: " + deserializedUser.getAge());
System.out.println("Пароль: " + deserializedUser.getPassword());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
мой пример
https://gitlab.com/synergy9980417/razdel2/5_5/-/blob/main/src/main/java/org/example/Main.java?ref_type=heads