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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Применение паттернов проектирования в Java

Паттерны проектирования находят широкое применение в Java благодаря её объектно-ориентированной природе и богатой экосистеме.

1. Разработка корпоративных приложений

Java часто используется для создания сложных серверных систем, таких как банковские платформы или системы управления контентом. В таких проектах паттерны проектирования помогают структурировать код.

Например:
Фреймворк Spring: Использует паттерн "Внедрение зависимостей" для управления жизненным циклом объектов. Это позволяет разработчикам сосредоточиться на бизнес-логике, а не на создании и настройке объектов.
Hibernate: Применяет паттерн "Фасад" для упрощения работы с базой данных, скрывая сложность SQL-запросов за простым API.


2. Создание библиотек и API

Паттерны проектирования упрощают создание библиотек, которые другие разработчики могут использовать.

Например:
В Java API паттерн "Итератор" используется в коллекциях (например, List или Set), чтобы обеспечить единообразный способ перебора элементов.
Паттерн "Фабричный метод" применяется в классе java.util.Calendar, где метод getInstance() создает объекты в зависимости от локализации.


3. Управление многопоточностью

Java широко используется для многопоточного программирования, и паттерны помогают управлять сложностью.

Например:
Паттерн "Синглтон" часто используется для создания единственного экземпляра менеджера ресурсов, такого как пул соединений с базой данных.
Паттерн "Монитор" встроен в Java на уровне языка (через ключевое слово synchronized), что упрощает синхронизацию потоков.


4. Упрощение тестирования

Паттерны способствуют написанию тестируемого кода. Например, использование "Внедрения зависимостей" позволяет легко заменять реальные компоненты на заглушки (mocks) во время тестирования.


Проблемы и критика паттернов проектирования

Хотя паттерны проектирования чрезвычайно полезны, их применение не лишено недостатков:
Избыточная сложность: Иногда использование паттерна усложняет код там, где можно было бы обойтись более простым решением.
Злоупотребление: Новички могут применять паттерны везде, даже когда они не нужны, что приводит к "паттерн-ориентированному" программированию.
Контекстная зависимость: Не все паттерны подходят для всех ситуаций. Например, "Синглтон" может быть проблематичным в многопоточных приложениях, если не позаботиться о синхронизации.

В Java-разработке важно понимать, когда и какой паттерн использовать, чтобы избежать ненужной сложности. Опытные разработчики часто комбинируют паттерны, адаптируя их под конкретные задачи.



Для более подробного изучения паттернов, предлагаю к просмотру 4 ранее записанных видео:

Порождающие паттерны проектирования на Java
Структурные паттерны проектирования на Java
Поведенческие паттерны проектирования на Java. Часть 1
Поведенческие паттерны проектирования на Java. Часть 2

Для полезного чтения, рекомендую сайт (может не работать без VPN)

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

Приступим'с


Архитектурный шаблон MVC в Java Spring: теория, правила, ошибки

MVC (Model–View–Controller) — это один из фундаментальных архитектурных паттернов в программной инженерии, направленный на разделение приложения на логически независимые компоненты. Его основная цель — изолировать пользовательский интерфейс от бизнес-логики и доступа к данным. В экосистеме Java фреймворк Spring реализует этот шаблон через модуль Spring MVC.

Теория: что такое MVC

Шаблон MVC подразумевает разделение приложения на три слоя:

Model (Модель)

Модель инкапсулирует бизнес-логику и данные приложения. Она может включать:
Сущности базы данных (например, классы с аннотацией @Entity).
Репозитории (
@Repository), обеспечивающие доступ к данным.
Сервисный слой (
@Service), реализующий бизнес-логику.
DTO (Data Transfer Objects) — объекты для обмена данными между слоями.


❗️Модель не должна зависеть от представления или контроллера.

View (Представление)

Представление отвечает за отображение информации пользователю. В Spring это обычно:
HTML-шаблоны (например, на Thymeleaf).
JSON-ответы (в случае REST API).
Альтернативные форматы (XML, PDF и пр.).
View должен быть как можно более простым и не содержать бизнес-логики.


Controller (Контроллер)

Контроллер — связующее звено между внешним миром (HTTP-запросами) и внутренними слоями приложения. Он:
Принимает входные данные.
Делегирует выполнение модели.
Выбирает и возвращает подходящее представление.
Контроллеры в Spring создаются с помощью аннотаций
@Controller или @RestController.

Жизненный цикл HTTP-запроса в Spring MVC


🔵Пользователь отправляет HTTP-запрос.
🔵DispatcherServlet перехватывает запрос.
🔵Система HandlerMapping находит соответствующий контроллер.
🔵Контроллер вызывает метод бизнес-логики через @Service.
🔵Сервис взаимодействует с репозиториями и возвращает результат.
🔵Контроллер передаёт данные во View (или сериализует их в JSON).
🔵Ответ возвращается клиенту.

Роль слоёв в приложении Spring


Контроллер
Контроллер должен быть «тонким» и не содержать бизнес-логики. Его задача — маршрутизировать запросы и собирать данные, необходимые представлению.

Пример (корректный):
@GetMapping("/users")
public List<UserDto> getUsers(Model model) {
return userService.getAllUsers();
}


Неправильный пример (контроллер содержит логику):
@GetMapping("/users")
public List<UserDto> getUsers(Model model) {
List<User> users = userRepository.findAll();
return users.stream()
.map(user -> new UserDto(user.getId(), user.getName()))
.collect(Collectors.toList());
}


Сервисный слой

Сервисный слой содержит бизнес-логику, правила обработки данных, валидацию, логи, обработку ошибок. Он изолирован от HTTP и представления. Сервисы не должны напрямую зависеть от Model, HttpServletRequest или аннотаций Spring MVC.

Пример:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;

public List<UserDto> getAllUsers() {
return userRepository.findAll().stream()
.map(user -> new UserDto(user.getId(), user.getName()))
.collect(Collectors.toList());
}
}


Репозиторий

Репозиторий отвечает за доступ к данным. Он инкапсулирует работу с базой и предоставляет CRUD-интерфейс.
public interface UserRepository extends JpaRepository<User, Long> {}


#Java #для_новичков #beginner #on_request #mvc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Распространённые ошибки

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