Java for Beginner
695 subscribers
605 photos
165 videos
12 files
938 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Распространённые ошибки

1. Логика в контроллерах
Признак: контроллер становится "сервисом", содержащим условия, циклы, доступ к БД. Это нарушает принципы чистой архитектуры и усложняет тестирование.

2. Использование Entity в представлении
Передача @Entity напрямую в шаблон может привести к:
Утечке данных (например, паролей).
Ошибкам LazyInitializationException.
Сильной связанности представления с базой данных.
Решение: использовать DTO или ViewModel.


3. Жёсткая связность между слоями
View не должно зависеть от Repository, а Controller — от Entity. Каждый слой должен взаимодействовать только с соседним.

4. Отсутствие DTO
Использование одной и той же модели во всех сценариях ведёт к путанице и проблемам безопасности. Лучше использовать отдельные классы:
UserCreateRequest
UserResponse
UserUpdateRequest


Рекомендации по проектированию

Структура проекта

Хорошей практикой является разделение кода по слоям:
com.example.myapp
├── controller
├── service
│ └── impl
├── repository
├── dto
├── model
├── config


URL-дизайн

Соблюдайте RESTful-стиль:
GET /users — получить список пользователей.
GET /users/{id} — получить конкретного пользователя.
POST /users — создать.
PUT /users/{id} — обновить.
DELETE /users/{id} — удалить.


Использование DTO
public class UserResponse {
private Long id;
private String name;
}


public class UserCreateRequest {
private String name;
private String email;
}


Расширения и адаптации MVC

SPA + API
При использовании Vue, React или Angular, представление полностью переносится на фронтенд. В этом случае Spring работает как REST API с @RestController, и классическая схема MVC трансформируется в «REST + JSON».

Поддержка реактивности
Spring WebFlux реализует неблокирующую модель с Mono и Flux, сохраняя при этом логическую структуру MVC. Подходит для высоконагруженных и асинхронных приложений.

Тестирование компонентов MVC


Контроллеры — @WebMvcTest, MockMvc.
Сервисы —
@SpringBootTest или с моками (@MockBean).
Репозитории —
@DataJpaTest.

#Java #для_новичков #beginner #on_request #mvc
👍5
После проведенного голосования, определилась тема для рассмотрения в выходные - JavaMailSender.

Спасибо предложившему 👏, интересная тема, даже не знал о существовании такого модуля Spring. Есть мысли потом сделать видео гайд. Как смотрите?

Ну и читаем'с статейку

(примечание - код взят с открытых источников и мной не тестировался, уж извините)


JavaMailSender в Spring Boot


JavaMailSender — это интерфейс из Spring Framework, который упрощает работу с JavaMail API (перешедшим на Jakarta EE) и позволяет отправлять электронные письма из приложений на Spring Boot. Он используется вместе с зависимостью spring-boot-starter-mail и предоставляет удобный API для работы с SMTP-серверами. Поддерживаются простые текстовые письма, HTML-сообщения, вложения, inline-ресурсы и шаблоны.

Установка зависимости

Для использования JavaMailSender добавьте зависимость в ваш проект. В Spring Boot 3.x используется jakarta.mail вместо javax.mail.

Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>


Gradle:
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.eclipse.angus:jakarta.mail:2.0.1'


Базовая настройка почтового клиента

Настройте параметры SMTP-сервера в файле application.properties или application.yml.

Пример для Gmail:
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=your.email@gmail.com
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true


Если используется application.yml:
spring:
mail:
host: smtp.gmail.com
port: 587
username: your.email@gmail.com
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
starttls:
enable: true

Примечание: Пароль лучше хранить в переменных окружения или внешних конфигурациях (например, Kubernetes Secrets) вместо прямого указания в файле.

Настройка для популярных SMTP-серверов

Настройки SMTP зависят от провайдера. Вот примеры для популярных сервисов.

Gmail
SMTP-сервер: smtp.gmail.com
Порт: 587 (STARTTLS) или 465 (SSL)
Рекомендуется использовать OAuth2 вместо пароля приложения.


Yandex
spring.mail.host=smtp.yandex.ru
spring.mail.port=465
spring.mail.username=your.email@yandex.ru
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable=true


Mail.ru
spring.mail.host=smtp.mail.ru
spring.mail.port=465
spring.mail.username=your.email@mail.ru
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable=true


Outlook / Office365
spring.mail.host=smtp.office365.com
spring.mail.port=587
spring.mail.username=your.email@outlook.com
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true


Amazon SES
spring.mail.host=email-smtp.us-east-1.amazonaws.com
spring.mail.port=587
spring.mail.username=your-ses-access-key
spring.mail.password=your-ses-secret-key
spring.mail.properties.mail.smtp.starttls.enable=true


Примечание: Для корпоративных SMTP-серверов уточняйте настройки у администратора. Для Amazon SES требуется настройка в AWS Console.


#Java #middle #on_request #JavaMailSender
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Настройка OAuth2 для Gmail

Для Gmail предпочтительно использовать OAuth2.

Пример конфигурации:
@Configuration
public class MailConfig {

@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("your.email@gmail.com");

Properties props = mailSender.getJavaMailProperties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");

// Настройка OAuth2 токена через Google API Client Library
// Подробности: https://developers.google.com/identity/protocols/oauth2
return mailSender;
}
}


Примечание: Зарегистрируйте приложение в Google Cloud Console и используйте библиотеку google-auth-library-oauth2-http для получения accessToken.

Создание сервиса отправки почты

Создадим сервис EmailService для отправки писем.

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private TemplateEngine templateEngine;

private static final Logger logger = LoggerFactory.getLogger(EmailService.class);

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}

public void sendHtmlEmail(String to, String subject, String htmlBody) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}

public void sendEmailWithAttachment(String to, String subject, String text, File attachment) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(text);
FileSystemResource file = new FileSystemResource(attachment);
helper.addAttachment(attachment.getName(), file);
mailSender.send(message);
}

@Async
public CompletableFuture<Void> sendSimpleEmailAsync(String to, String subject, String text IMPORTANT: text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
message.setText(text);
mailSender.send(message);
return CompletableFuture.completedFuture(null);
}

public void sendTemplatedEmail(String to, String subject, String username) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
Context context = new Context();
context.setVariable("username", username);
String htmlBody = templateEngine.process("email-template", context);
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}
}


Конфигурация асинхронности:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
}


#Java #middle #on_request #JavaMailSender
👍1
Примеры кода

Простой текст:
SimpleMailMessage message = new SimpleMailMessage();
message.setTo("user@example.com");
message.setSubject("Тестовое письмо");
message.setText("Привет из Spring Boot!");
mailSender.send(message);


HTML-письмо:
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("HTML письмо", "UTF-8", null));
helper.setText("<h1>Привет!</h1><p>Это HTML письмо.</p>", true);
mailSender.send(message);


Вложение:

File file = new File("/path/to/file.pdf");
FileSystemResource resource = new FileSystemResource(file);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("Письмо с вложением", "UTF-8", null));
helper.setText("Смотри вложение!");
helper.addAttachment("file.pdf", resource);
mailSender.send(message);


Обработка ошибок и логирование

JavaMailSender может выбрасывать исключения, которые нужно обрабатывать.

Основные исключения:
MailAuthenticationException
MailSendException
MessagingException


Пример отлова:
try {
mailSender.send(message);
} catch (MailException e) {
logger.error("Ошибка отправки письма", e);
throw e;
}


Повторные попытки
Используйте @Retryable из Spring Retry:
@Retryable(value = MailException.class, maxAttempts = 3)
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}


Зависимость:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>


Мониторинг и метрики

Для продакшен-приложений полезно отслеживать метрики отправки писем с помощью Spring Actuator:
@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private MeterRegistry meterRegistry;

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
try {
mailSender.send(message);
meterRegistry.counter("email.sent.success").increment();
} catch (MailException e) {
meterRegistry.counter("email.sent.failure").increment();
throw e;
}
}
}


Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>


Безопасность и защита данных

Никогда не храните пароли в открытом виде в git-репозиториях.

Рекомендации:
Используйте переменные окружения или секреты (например, Kubernetes Secrets).
Применяйте Spring Cloud Vault или HashiCorp Vault.
Используйте шифрование с Jasypt.
Настройте OAuth2 для Gmail.


Пример с переменными окружения:
spring.config.import=optional:configserver:http://config-server
spring.mail.password=${MAIL_PASSWORD}


#Java #middle #on_request #JavaMailSender
👍1
Тестирование отправки почты

Интеграционные тесты с Testcontainers:
@Testcontainers
@SpringBootTest
public class EmailServiceTest {

@Container
private static final GreenMailContainer greenMail = new GreenMailContainer();

@Autowired
private EmailService emailService;

@Test
void shouldSendEmail() {
emailService.sendSimpleEmail("test@example.com", "Test Subject", "Test Body");
assertEquals(1, greenMail.getReceivedMessages().length);
}
}


Зависимость:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>greenmail</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>


Unit-тестирование:
@MockBean
private JavaMailSender mailSender;

@Test
void shouldSendMail() {
SimpleMailMessage message = new SimpleMailMessage();
emailService.sendSimpleEmail("to", "subject", "body");
verify(mailSender, times(1)).send(any(SimpleMailMessage.class));
}


Интеграция с очередями

Для отложенной отправки используйте RabbitMQ(ну или другой брокер сообщений):

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private RabbitTemplate rabbitTemplate;

public void sendEmailToQueue(String to, String subject, String text) {
EmailMessage email = new EmailMessage(to, subject, text);
rabbitTemplate.convertAndSend("emailQueue", email);
}

@RabbitListener(queues = "emailQueue")
public void processEmail(EmailMessage email) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email.getTo());
message.setSubject(email.getSubject());
message.setText(email.getText());
mailSender.send(message);
}

@Data
static class EmailMessage {
private String to;
private String subject;
private String text;
}
}


Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>


Распространенные ошибки и подводные камни

Неверная конфигурация SMTP (порт, шифрование).
Проблемы с двухфакторной авторизацией (особенно Gmail).
Ограничения на массовую рассылку (например, Gmail: ~500 писем/день для бесплатных аккаунтов).
Проблемы с кодировкой (например, кириллица в теме письма).
Отсутствие MIME-типа у вложений.
Блокировка сервером (например,
Mail.ru требует DKIM и SPF).

Решение для кодировки:
helper.setSubject(MimeUtility.encodeText("Тема на кириллице", "UTF-8", null));


#Java #middle #on_request #JavaMailSender
👍1
После проведенного голосования, определилась тема для рассмотрения в выходные - Stream Api

Но поскольку принято решение показать это все на видео, в рамках встречи, то перейдем к второму финалисту - Maven.
Тем более он давно напрашивается на цикл статей
😏

Ну читаем'с затравочную статейку


Введение в Apache Maven: автоматизация сборки и управления проектами

Apache Maven — это мощный инструмент для автоматизации сборки, управления зависимостями и проектной документации, широко используемый в экосистеме Java. Эта статья предназначена для тех, кто только начинает знакомство с Maven и хочет понять, зачем он нужен, как работает и какую роль может играть в разработке программного обеспечения.

Что такое Maven

Maven был создан как ответ на потребность в стандартизированной, предсказуемой и удобной системе сборки Java-проектов. Его название происходит от идишского слова «знаток» — и это хорошо отражает его назначение. Maven берет на себя множество задач, связанных с управлением проектом: от загрузки библиотек до сборки артефактов и генерации документации.

По сути, Maven — это система сборки, основанная на декларативном описании проекта в файле pom.xml (Project Object Model). Вместо написания множества сценариев разработчик указывает, что именно нужно сделать, а Maven сам знает, как это реализовать.

Зачем нужен Maven

В проектах на Java быстро возникает необходимость управлять зависимостями, компиляцией, тестами и выпуском новых версий. Раньше всё это приходилось настраивать вручную — с помощью антипаттернов вроде «анархичной сборки» на Ant, bash-скриптах и слабо задокументированных процедурах.

Maven решает эти проблемы:
Централизованное управление зависимостями: подключение внешних библиотек через репозитории.
Повторяемая сборка: независимо от среды результат будет одинаков.
Стандартизация структуры проекта.
Интеграция с инструментами тестирования, анализа качества кода и CI/CD.
Простота подключения плагинов и расширение функциональности без переписывания логики сборки.


Как работает Maven

Maven использует модель жизненного цикла сборки, разбитую на фазы: compile, test, package, install, deploy и другие. Когда вы вызываете mvn install, Maven проходит через все фазы от начала до указанной, автоматически выполняя нужные действия. Плагины реализуют конкретные шаги на каждой фазе.

Файл pom.xml описывает проект: его артефакт, координаты, зависимости, плагины и профили. Maven загружает зависимости из центрального репозитория, при необходимости кешируя их в локальном.

Репозитории делятся на:
Локальный (на вашей машине)
Центральный (официальный репозиторий Maven)
Удалённые (частные, корпоративные и т. д.)


Преимущества Maven

Одно из главных достоинств Maven — подход «конвенция важнее конфигурации». Он предполагает стандартную структуру каталогов и поведения, которую можно переопределить, но в большинстве случаев она устраивает по умолчанию. Это упрощает вхождение в проект новым разработчикам и снижает объем ручной настройки.

Maven обеспечивает:
Унифицированный подход к сборке.
Возможность многомодульной организации.
Расширяемость за счёт системы плагинов.
Широкую поддержку со стороны экосистемы (IDE, CI-систем, репозиториев).


Когда Maven может не подойти

Несмотря на свои преимущества, Maven не универсален. В проектах с сильно нестандартной логикой сборки или с большим количеством нестандартных шагов может потребоваться более гибкий инструмент, такой как Gradle. Также Maven изначально ориентирован на Java, и для других языков (например, JavaScript или Python) он не является естественным выбором.

#Java #middle #on_request #Maven
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
После проведенного голосования, из двух победителей я выбрал тему для рассмотрения в выходные - ObjectMapper

Несмотря на то, что на канале мы уже рассматривали этот класс (статья), я подготовил для вас еще одну, для расширения вопроса 🤫

Ну читаем'с статейку


ObjectMapper в Java (Jackson)

1. Что такое ObjectMapper и зачем он нужен?

ObjectMapper — это основная точка входа в библиотеку Jackson (или jackson-databind), которая предоставляет мощный инструментарий для сериализации Java-объектов в JSON (и другие форматы, такие как XML, YAML) и десериализации JSON обратно в Java-объекты. Он является ключевым компонентом для работы с JSON в Java-приложениях, особенно в RESTful API, микросервисах и интеграциях с внешними системами.


Зачем нужен ObjectMapper?
Сериализация: Преобразование Java-объектов (POJO) в JSON для передачи данных по сети, сохранения в файлы или базы данных.
Десериализация: Преобразование JSON в Java-объекты для обработки данных в приложении.
Интеграция: Упрощение работы с API, где JSON является стандартом обмена данными.
Гибкость: Поддержка сложных структур данных, кастомных форматов, аннотаций и расширений для специфических сценариев.

ObjectMapper широко используется в веб-приложениях (например, со Spring), для работы с NoSQL-базами (MongoDB, Elasticsearch), а также в системах обмена сообщениями (Kafka, RabbitMQ).


2. Как работает ObjectMapper?

ObjectMapper — это многофункциональный класс, который использует рефлексию и аннотации для анализа структуры Java-объектов и их преобразования в JSON и обратно.

Он состоит из нескольких ключевых компонентов:
AnnotationIntrospector: Отвечает за чтение аннотаций (например,
@JsonProperty, @JsonIgnore) для настройки правил сериализации/десериализации.
SerializerProvider / DeserializerProvider: Управляют процессом преобразования объектов в JSON и обратно, используя зарегистрированные сериализаторы и десериализаторы.
TypeFactory: Анализирует и обрабатывает типы данных, включая generics и сложные структуры.
ObjectReader / ObjectWriter: Объекты для чтения и записи данных, оптимизированные для конкретных операций.


Работа в памяти

Сериализация:
ObjectMapper анализирует структуру объекта через рефлексию или аннотации.
Создает промежуточное представление объекта в виде дерева (JsonNode) в памяти, если требуется (например, для сложных преобразований).
Преобразует объект в JSON-строку или поток байтов, используя ObjectWriter.
Использует StringBuilder или аналогичные структуры для формирования строки JSON, минимизируя аллокации памяти.
Для больших объектов может использоваться потоковая обработка (stream-based), чтобы избежать загрузки всего объекта в память.


Десериализация:
Парсит JSON (обычно через JsonParser), создавая поток токенов (JSON tokens).
Сопоставляет токены с полями Java-объекта, используя рефлексию или конструкторы/сеттеры.
Создает экземпляры объектов в куче (heap), что может быть затратно для больших JSON или сложных графов объектов.
Поддерживает ленивую загрузку (lazy loading) для оптимизации памяти в случае работы с большими данными.


Управление памятью:
ObjectMapper кэширует метаданные о классах (например, структуру полей и аннотаций) для повторного использования, что снижает накладные расходы.
Для больших JSON рекомендуется использовать потоковый API (JsonParser / JsonGenerator), чтобы минимизировать использование памяти.
В случае сложных графов объектов (с циклическими ссылками) используются механизмы, такие как
@JsonIdentityInfo, для предотвращения бесконечной рекурсии и переполнения стека.

Важно: ObjectMapper не является потокобезопасным (thread-safe). Создание экземпляра ObjectMapper — дорогостоящая операция из-за инициализации кэшей и конфигурации. Поэтому рекомендуется переиспользовать один экземпляр ObjectMapper в приложении, например, через синглтон или внедрение зависимостей в Spring.


#Java #middle #on_request #ObjectMapper
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
3. Основные возможности ObjectMapper

ObjectMapper предоставляет широкий набор функций для настройки сериализации и десериализации.

Вот ключевые возможности:

3.1. Базовые операции

import com.fasterxml.jackson.databind.ObjectMapper;

public class Example {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();

// POJO
public class User {
private String name;
private int age;

// Getters and setters
}

// Сериализация
User user = new User("Alice", 30);
String json = mapper.writeValueAsString(user);
System.out.println(json); // {"name":"Alice","age":30}

// Десериализация
String jsonInput = "{\"name\":\"Bob\",\"age\":25}";
User deserializedUser = mapper.readValue(jsonInput, User.class);
System.out.println(deserializedUser.getName()); // Bob
}
}


3.2. Конфигурация ObjectMapper

ObjectMapper позволяет настраивать поведение через методы конфигурации:
ObjectMapper mapper = new ObjectMapper();

// Игнорировать неизвестные поля при десериализации
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// Включить форматирование JSON (pretty print)
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

// Игнорировать null-поля при сериализации
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);


3.3. Аннотации

Jackson предоставляет множество аннотаций для управления сериализацией/десериализацией:
@JsonProperty("customName"): Переименовывает поле в JSON.
@JsonIgnore: Исключает поле из сериализации/десериализации.
@JsonInclude(JsonInclude.Include.NON_NULL): Исключает null-поля.
@JsonFormat: Задает формат для дат, чисел и других типов.
@JsonTypeInfo и @JsonSubTypes: Поддержка полиморфизма.


Пример:
public class User {
@JsonProperty("full_name")
private String name;

@JsonIgnore
private String password;

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;
}


3.4. Работа с Generics и сложными типами

Для работы с коллекциями или сложными типами используется TypeReference:
List<User> users = mapper.readValue(json, new TypeReference<List<User>>(){});


3.5. Потоковая обработка

Для больших данных можно использовать потоковый API:
JsonGenerator generator = mapper.getFactory().createGenerator(new FileOutputStream("output.json"));
generator.writeStartObject();
generator.writeStringField("name", "Alice");
generator.writeEndObject();
generator.close();


3.6. Кастомные сериализаторы/десериализаторы


Для нестандартных типов данных можно написать свои сериализаторы:
public class CustomSerializer extends StdSerializer<CustomType> {
public CustomSerializer() {
super(CustomType.class);
}

@Override
public void serialize(CustomType value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("custom", value.getValue());
gen.writeEndObject();
}
}

// Регистрация
mapper.registerModule(new SimpleModule().addSerializer(CustomType.class, new CustomSerializer()));


#Java #middle #on_request #ObjectMapper
👍2
4. Взаимодействие с Spring

ObjectMapper глубоко интегрирован с Spring Framework, особенно в модуле Spring Web (REST API). Spring использует Jackson для обработки JSON в контроллерах и взаимодействия с клиентами через HTTP.

4.1. Spring Boot и Jackson

Spring Boot автоматически настраивает ObjectMapper как бин, если в зависимостях есть jackson-databind. Вы можете кастомизировать его через свойства или конфигурацию.

Конфигурация через application.properties:
spring.jackson.serialization.indent-output=true
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.date-format=yyyy-MM-dd


Кастомизация через Java-конфигурацию:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
}


4.2. Использование в контроллерах

Spring автоматически использует ObjectMapper для сериализации ответов и десериализации запросов:

@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public User createUser(@RequestBody User user) {
// user автоматически десериализуется из JSON
return user;
}

@GetMapping
public List<User> getUsers() {
// Список автоматически сериализуется в JSON
return List.of(new User("Alice", 30));
}
}


4.3. Кастомизация для HTTP

Spring использует MappingJackson2HttpMessageConverter для обработки JSON.

Вы можете настроить его, чтобы использовать кастомный ObjectMapper:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(customObjectMapper());
converters.add(converter);
}

@Bean
public ObjectMapper customObjectMapper() {
return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true);
}
}



5. Взаимодействие с другими библиотеками


ObjectMapper часто используется в связке с другими библиотеками, которые работают с JSON или требуют сериализации/десериализации.

5.1. MongoDB

Spring Data MongoDB использует Jackson для сериализации объектов в BSON (бинарный JSON).

Вы можете настроить MongoTemplate для использования кастомного ObjectMapper:
@Configuration
public class MongoConfig {
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return new MongoTemplate(factory, new MappingMongoConverter(new DefaultDbRefResolver(factory), new MongoMappingContext()));
}
}


5.2. Kafka

В Apache Kafka ObjectMapper используется для сериализации/десериализации сообщений.

Например:
@Bean
public KafkaTemplate<String, User> kafkaTemplate(ProducerFactory<String, User> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}

@KafkaListener(topics = "user-topic")
public void listen(@Payload String json, ConsumerRecord<String, String> record) throws IOException {
User user = new ObjectMapper().readValue(json, User.class);
System.out.println("Received: " + user);
}


5.3. Elasticsearch

Spring Data Elasticsearch также использует Jackson для работы с JSON. Вы можете настроить RestHighLevelClient с кастомным ObjectMapper.

5.4. Другие форматы

Jackson поддерживает модули для работы с другими форматами (XML, YAML, CBOR). Например:
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
String yaml = yamlMapper.writeValueAsString(user);


#Java #middle #on_request #ObjectMapper
👍2
6. Оптимизация и лучшие практики

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


Потоковая обработка:
Для больших JSON используйте JsonParser и JsonGenerator вместо полной загрузки в JsonNode.

Кэширование:
ObjectMapper автоматически кэширует метаданные классов. Избегайте частого создания новых экземпляров.

Обработка ошибок:
Обрабатывайте исключения, такие как JsonProcessingException, для повышения надежности.
Настройте DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES для игнорирования неизвестных полей.


Тестирование:
Проверяйте сериализацию/десериализацию в тестах, особенно для сложных объектов.
Используйте библиотеки, такие как jsonassert, для сравнения JSON.


Безопасность:
Избегайте десериализации непроверенных данных, чтобы предотвратить атаки (например, уязвимости в стиле XXE).
Используйте
@JsonIgnoreProperties для ограничения полей.


7. Распространенные проблемы и их решения

Циклические ссылки:

Используйте @JsonIdentityInfo для обработки циклических зависимостей:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class User {
private Long id;
private List<User> friends;
}


Неизвестные поля:
Настройте mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).

Неправильные форматы дат:
Используйте @JsonFormat или настройте глобальный формат через mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd")).

Производительность:
Для больших объектов используйте потоковую обработку или минимизируйте использование JsonNode.


8. Пример реального сценария

Допустим, вы разрабатываете REST API для управления пользователями. JSON-запросы и ответы должны быть в определенном формате, а некоторые поля — опциональными.

POJO:
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
@JsonProperty("full_name")
private String name;

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;

@JsonIgnore
private String password;

// Getters and setters
}


Контроллер:
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
// Логика сохранения
return ResponseEntity.ok(user);
}
}


Конфигурация:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()); // Для LocalDate
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}


#Java #middle #on_request #ObjectMapper
👍2
Обзор IO и NIO в Java

В Java операции ввода-вывода реализуются через два основных пакета: `java.io` (классический IO) и `java.nio` (New Input/Output, или NIO), с дополнительными улучшениями в NIO.2, представленными в Java 7. Эти API предназначены для работы с файлами, сетевыми соединениями и другими задачами ввода-вывода, но существенно различаются по архитектуре, производительности и управлению ресурсами.



Классический IO (
java.io)

Пакет `java.io`, появившийся в Java 1.0, предоставляет блокирующий подход к операциям ввода-вывода, ориентированный на потоковую обработку данных. Это делает его простым и интуитивно понятным для базовых задач, таких как чтение файлов или работа с консолью, но ограничивает масштабируемость в высоконагруженных приложениях. Он работает в блокирующем режиме: каждая операция, например чтение из файла или сокета, блокирует вызывающий поток до завершения. Это означает, что для обработки множества соединений требуется создание пула потоков, что увеличивает потребление памяти, так как каждый поток в JVM занимает около 1 МБ стека по умолчанию.

Данные обрабатываются как последовательный поток байтов или символов, что не позволяет перемещаться назад или вперед по данным без дополнительного кэширования. Потоки являются однонаправленными, то есть предназначены либо для чтения, либо для записи. С точки зрения памяти, `java.io` использует память кучи JVM. Буферизированные потоки снижают количество системных вызовов за счет внутренних массивов (обычно размером 8192 байт), но увеличивают потребление памяти. Отсутствие поддержки прямой памяти приводит к дополнительным накладным расходам на копирование данных между JVM и операционной системой.

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

При работе с символами необходимо явно указывать кодировку (`Charset`), чтобы избежать проблем с некорректным отображением текста. Также важно использовать конструкцию `try-with-resources`, введенную в Java 7, для предотвращения утечек ресурсов, так как потоки требуют явного закрытия. Для обработки множества соединений требуется пул потоков, что усложняет код и увеличивает потребление памяти.

#Java #middle #on_request #IO #NIO
👍3
Основные классы и интерфейсы java.io

- InputStream: Абстрактный класс для чтения байтовых потоков из различных источников, таких как файлы или сокеты.
- OutputStream: Абстрактный класс для записи байтовых потоков.
- FileInputStream: Читает байты из файла, напрямую взаимодействуя с файловой системой.
- FileOutputStream: Записывает байты в файл.
- Reader: Абстрактный класс для чтения символьных потоков с учетом кодировок.
- Writer: Абстрактный класс для записи символьных потоков.
- FileReader: Читает символы из файла, преобразуя байты в символы с учетом кодировки.
- FileWriter: Записывает символы в файл.
- BufferedInputStream: Буферизирует байтовый ввод, снижая количество системных вызовов.
- BufferedOutputStream: Буферизирует байтовый вывод.
- BufferedReader: Буферизирует символьный ввод, поддерживает чтение строк (`readLine()`).
- BufferedWriter: Буферизирует символьный вывод.
- File: Представляет файл или директорию в файловой системе, позволяет проверять существование, создавать или удалять файлы.
- Socket: Реализует клиентские TCP-соединения для сетевого ввода-вывода.
- ServerSocket: Реализует серверные TCP-соединения.
- DataInputStream: Читает примитивные типы данных (int, double и т.д.) из байтового потока.
- DataOutputStream: Записывает примитивные типы данных в байтовый поток.
- ObjectInputStream: Десериализует объекты из потока.
- ObjectOutputStream: Сериализует объекты в поток.


Пример использования
Чтение файла с использованием `BufferedReader`:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}



#Java #middle #on_request #IO #NIO
👍3
NIO (java.nio) и NIO.2 (java.nio.file)

NIO, представленный в Java 1.4, был разработан для устранения ограничений классического IO, предлагая неблокирующий и буферно-ориентированный подход. NIO.2, добавленный в Java 7, расширил функциональность, включив мощный API для работы с файловой системой и асинхронные каналы. Эти API идеально подходят для высоконагруженных приложений, таких как серверы, обрабатывающие тысячи соединений, или для работы с большими файлами.

NIO поддерживает неблокирующий режим, в котором каналы могут быть настроены для обработки множества соединений одним потоком через селекторы. NIO.2 добавляет асинхронный режим, где каналы используют пулы потоков (по умолчанию ForkJoinPool.commonPool()) для выполнения операций без блокировки вызывающего потока. Данные обрабатываются через буферы, которые передаются каналам, что позволяет гибко манипулировать данными, перемещаясь вперед и назад по буферу. Каналы являются двунаправленными, поддерживая как чтение, так и запись.

С точки зрения памяти, NIO поддерживает прямую память через DirectByteBuffer, выделяемую вне кучи JVM в нативной памяти. Это минимизирует копирование данных (zero-copy) при передаче в системные вызовы, улучшая производительность, но требует осторожного управления, так как сборщик мусора не контролирует эту память. Неправильное использование может привести к утечкам (OutOfMemoryError: Direct buffer memory). Размер буфера должен быть оптимизирован: слишком маленький увеличивает количество операций, слишком большой потребляет лишнюю память. Использование селекторов позволяет одному потоку обрабатывать тысячи соединений, снижая потребность в потоках и потребление памяти. Для больших файлов каналы и отображение в память минимизируют системные вызовы, улучшая производительность.

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

Работа с NIO сложнее, чем с IO, из-за необходимости управлять буферами, включая их позицию, лимит и емкость, а также методы flip(), compact() и clear(). Каналы требуют явной конфигурации для переключения между блокирующим и неблокирующим режимами. Управление селекторами предполагает понимание событий, таких как готовность к чтению или записи, и их жизненного цикла. Прямая память требует осторожного освобождения ресурсов, например с использованием sun.misc.Cleaner. Асинхронные каналы в NIO.2 работают с Future или CompletionHandler, что добавляет сложность, но повышает гибкость. Мониторинг файловой системы может быть чувствителен к реализации, особенно на Windows, где потребляет больше ресурсов.


Основные классы и интерфейсы NIO (java.nio)

Buffer: Абстрактный класс для буферов данных, обеспечивающий гибкую работу с данными.
ByteBuffer: Буфер для работы с байтами, поддерживает прямую и непрямую память.
CharBuffer: Буфер для работы с символами.
MappedByteBuffer: Буфер для отображения файла в память, минимизирующий копирование данных.
Channel: Интерфейс для каналов ввода-вывода, обеспечивающий эффективную передачу данных.
FileChannel: Для чтения/записи файлов, поддерживает отображение в память.
SocketChannel: Для TCP-соединений, поддерживает неблокирующий режим.
ServerSocketChannel: Для серверных TCP-соединений.
DatagramChannel: Для UDP-соединений.
Selector: Мультиплексор для отслеживания событий на множестве каналов.
SelectionKey: Представляет регистрацию канала в селекторе и его события (OP_READ, OP_WRITE, OP_ACCEPT).
CharsetDecoder: Для преобразования байтов в символы с учетом кодировок.
CharsetEncoder: Для преобразования символов в байты.


#Java #middle #on_request #IO #NIO
👍3
Основные классы и интерфейсы NIO.2 (java.nio.file и асинхронные каналы)

Path: Представляет путь в файловой системе, более гибкий аналог File.
Paths: Фабрика для создания объектов Path.
Files: Утилитный класс для операций с файлами (чтение, запись, копирование, управление атрибутами).
FileSystem: Представляет файловую систему, предоставляет доступ к Path и другим объектам.
FileSystems: Фабрика для создания объектов FileSystem.
WatchService: Для мониторинга изменений в файловой системе (например, создание/удаление файлов).
AsynchronousFileChannel: Для асинхронного чтения/записи файлов.
AsynchronousSocketChannel: Для асинхронных TCP-соединений.
AsynchronousServerSocketChannel: Для асинхронных серверных TCP-соединений.
FileVisitor: Интерфейс для обхода дерева файловой системы.
StandardOpenOption: Опции для открытия файлов/каналов (например, READ, WRITE, APPEND).


Пример использования NIO
Простой сервер с использованием NIO:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress("localhost", 8080));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

ByteBuffer buffer = ByteBuffer.allocate(1024);

while (true) {
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();

if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
buffer.clear();
int bytesRead = client.read(buffer);
if (bytesRead == -1) {
client.close();
} else {
buffer.flip();
client.write(buffer);
}
}
}
}
}
}


Пример использования NIO.2
Асинхронное чтение файла:

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;

public class AsyncFileRead {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);

Future<Integer> result = fileChannel.read(buffer, 0);
while (!result.isDone()) {
System.out.println("Waiting for read operation...");
Thread.sleep(100);
}

buffer.flip();
byte[] data = new byte[buffer.limit()];
buffer.get(data);
System.out.println(new String(data));
fileChannel.close();
}
}



#Java #middle #on_request #IO #NIO
👍3
Сравнение IO и NIO

Классический IO использует потоковую модель, где данные читаются или записываются последовательно, без возможности перемещения назад или вперед. Он работает в блокирующем режиме, требуя отдельного потока на каждое соединение, что подходит для приложений с небольшим количеством соединений и высокой пропускной способностью, но ограничивает масштабируемость. API java.io интуитивно понятно и просто в использовании, что делает его предпочтительным для начинающих или простых задач. Память кучи, используемая IO, приводит к дополнительным накладным расходам на копирование данных, а потребление памяти потоками делает его неэффективным для высоконагруженных систем.

NIO и NIO.2 используют буферно-канальную модель, где данные обрабатываются в буферах, передаваемых каналам, что позволяет гибко манипулировать данными. Каналы являются двунаправленными и поддерживают неблокирующий режим, позволяя одному потоку обрабатывать множество соединений через селекторы. Асинхронные каналы в NIO.2 дополнительно повышают гибкость. Поддержка прямой памяти минимизирует копирование данных, улучшая производительность, но требует осторожного управления. API NIO сложнее, требуя понимания буферов, каналов и селекторов, но оно оправдано для высоконагруженных приложений или работы с большими файлами.

Для работы с файлами IO предлагает менее гибкие инструменты, тогда как NIO.2 предоставляет более мощные и удобные классы. Для сетевых операций NIO обеспечивает лучшую масштабируемость благодаря селекторам и неблокирующему режиму.



Практические рекомендации

🔵При выборе между IO и NIO учитывайте требования приложения. Используйте java.io для простых задач, таких как чтение конфигурационных файлов или обработка небольших текстовых данных, где важна простота кода. NIO и NIO.2 предпочтительны для высоконагруженных серверов, работы с большими файлами или мониторинга файловой системы, где требуется масштабируемость и производительность.

🔵Для оптимизации памяти в IO применяйте буферизированные потоки, чтобы сократить системные вызовы, но учитывайте потребление памяти потоками. В NIO используйте прямую память для сетевых операций, чтобы минимизировать копирование данных, но следите за утечками памяти. Оптимизируйте размер буферов: 8 КБ для сетевых операций и 64 КБ для файловых. Для больших файлов используйте отображение в память, чтобы минимизировать системные вызовы.

🔵С точки зрения производительности, избегайте прямых операций без буферизации в IO, так как они вызывают системные вызовы для каждого байта. В NIO используйте селекторы для масштабируемой обработки соединений и оптимизируйте работу с буферами. Для больших файлов применяйте асинхронные каналы.

🔵Обрабатывайте исключения, такие как IOException в IO и ClosedByInterruptException или AsynchronousCloseException в NIO, и используйте try-with-resources для автоматического закрытия ресурсов. Проверяйте состояние буферов и каналов, чтобы избежать ошибок, связанных с неполным чтением или записью.

🔵Тестируйте производительность на реальных данных, учитывая тип файловой системы и сетевые условия. Используйте профилировщики, такие как VisualVM, JProfiler или YourKit, для анализа узких мест. Добавляйте логирование для отслеживания операций, особенно в асинхронных приложениях. Учитывайте кроссплатформенные различия: методы NIO.2 более устойчивы, но мониторинг файловой системы может быть менее эффективным на Windows.


#Java #middle #on_request #IO #NIO
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3