Библиотека Java разработчика
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
نمایش بیشتر📈 تحلیل کانال تلگرام Библиотека Java разработчика
کانال Библиотека Java разработчика (@bookjava) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 10 005 مشترک است و جایگاه 12 110 را در دسته فناوری و برنامهها و رتبه 63 455 را در منطقه روسيا دارد.
📊 شاخصهای مخاطب و پویایی
از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 10 005 مشترک جذب کرده است.
بر اساس آخرین دادهها در تاریخ 25 اکتبر, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر -54 و در ۲۴ ساعت گذشته برابر -2 بوده و همچنان دسترسی گستردهای حفظ شده است.
- وضعیت تأیید: تأیید نشده
- نرخ تعامل (ER): میانگین تعامل مخاطب 7.79% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 3.74% واکنش نسبت به کل مشترکان کسب میکند.
- دسترسی پستها: هر پست به طور میانگین 780 بازدید دریافت میکند. در اولین روز معمولاً 374 بازدید جمعآوری میشود.
- واکنشها و تعامل: مخاطبان بهطور فعال حمایت میکنند؛ میانگین واکنش به هر پست 4 است.
- علایق موضوعی: محتوا بر موضوعات کلیدی مانند string, интерфейс, строка, boot, api تمرکز دارد.
📝 توضیح و سیاست محتوایی
نویسنده این فضا را محل بیان دیدگاههای شخصی توصیف میکند:
“📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.
По всем вопросам @evgenycarter
РКН clck.ru/3KoGeP”
به لطف بهروزرسانیهای پرتکرار (آخرین داده در تاریخ 26 اکتبر, 2026)، کانال همواره بهروز و دارای دسترسی بالاست. تحلیلها نشان میدهد مخاطبان بهطور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامهها تبدیل کردهاند.
@Transactional в Spring
Сейчас покажу вам один распространённый анти-паттерн, который легко пропустить — вызов транзакционного метода внутри того же класса.
📌 Пример:
@Service
public class UserService {
@Transactional
public void registerUser(UserDto dto) {
saveUser(dto);
}
@Transactional
public void saveUser(UserDto dto) {
// сохранение пользователя
}
}
💥 Проблема:
Spring не применит транзакцию к saveUser(), потому что вызов происходит внутри одного и того же бина — минуя прокси.
Spring AOP работает через прокси, и @Transactional "срабатывает", только если метод вызывается извне, через прокси-объект.
⚠️ Это может привести к очень странным багам: вы думаете, что транзакция есть, а её нет.
💡 Как исправить:
1. Вынести метод в отдельный бин:
@Service
public class UserSaver {
@Transactional
public void save(UserDto dto) {
// сохраняем
}
}
@Service
public class UserService {
private final UserSaver saver;
public UserService(UserSaver saver) {
this.saver = saver;
}
public void registerUser(UserDto dto) {
saver.save(dto);
}
}
2. Или использовать TransactionTemplate вручную.
✅ Всегда проверяйте, как вызываются методы с @Transactional. Особенно при рефакторинге!
📲 Мы в MAX
👉@BookJava@Value vs @ConfigurationProperties — кого выбрать?
Часто вижу, как даже опытные разработчики по привычке используют @Value для инъекции конфигурации:
@Value("${app.timeout}")
private Duration timeout;
⚠️ Но с ростом приложения @Value становится хрупким и неудобным.
📌 Лучший подход — использовать @ConfigurationProperties:
@ConfigurationProperties(prefix = "app")
public record AppProperties(Duration timeout, String apiKey) {}
@Bean
@ConfigurationPropertiesBinding
public AppProperties appProperties() {
return new AppProperties();
}
✅ Преимущества @ConfigurationProperties:
- 💡 Группирует настройки логически
- 🔍 Работает с валидацией (@Validated, @NotNull, и т.д.)
- 📚 Отлично поддерживается IDE (автокомплит, рефакторинг)
- 🔧 Удобно тестировать и мокать
🆕 Начиная с Spring Boot 2.2+, можно использовать record-классы и просто зарегистрировать бин через @EnableConfigurationProperties:
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class AppConfig {}
Так что, если у вас в проекте до сих пор десятки @Value — самое время навести порядок.
📲 Мы в MAX
👉@BookJava@Value в Spring — это ловушка, если вы используете списки или map'ы
Многие знают, что можно заинжектить список строк из application.yml вот так:
app:
langs:
- en
- fr
- de
@Value("${app.langs}")
private List<String> langs;
Но знаете, что вы получите?
⚠️ ОШИБКУ. @Value не умеет парсить YAML-массивы. Он ожидает строку, и даже с CSV-строкой (en,fr,de) — всё не так очевидно: Spring не применяет ConversionService для списков.
📌 Решение — использовать @ConfigurationProperties:
app:
langs:
- en
- fr
- de
@ConfigurationProperties(prefix = "app")
@Component
public class AppProps {
private List<String> langs;
// геттеры/сеттеры
}
💡 Профит:
- работает с List, Map, вложенными объектами;
- валидация через @Validated и @NotEmpty;
- легко покрыть тестами;
- меньше магии.
⚠️ @Value хорош для простых скаляров. Всё остальное — через @ConfigurationProperties.
📲 Мы в MAX
👉@BookJavaOptional — это не замена null везде и всегда
Привет! Сегодня хочу поделиться одной из часто встречающихся ошибок при использовании Optional в Java.
Многие разработчики, особенно начинающие, начинают использовать Optional везде, где может быть null, думая, что это автоматически делает код "безопасным". Но так ли это?
📌 Ключевая идея Optional — сигнализировать о возможном отсутствии значения в результате вызова метода.
А не заменять все поля и параметры на Optional.
Примеры плохой практики:
public class User {
private Optional<String> name; // ❌ Не нужно так делать
}
Почему это плохо:
- Увеличивается сложность сериализации (особенно с Jackson, GSON).
- Не соответствует архитектурной задумке: Optional — это не контейнер для полей.
- Проблемы с JPA (Hibernate не дружит с Optional-полями).
- Понижается читаемость кода.
💡 Лучше использовать Optional вот так:
public Optional<User> findUserById(Long id) {
// Возвращаем Optional, потому что пользователь может не существовать
}
То есть Optional — это про контракт на метод, а не про хранение данных.
Если кратко:
- ✅ Используй Optional в сигнатурах методов, когда результат может отсутствовать.
- ❌ Не используй Optional в полях и параметрах конструктора.
А ты как используешь Optional в проектах? Был ли опыт с его неправильным применением? Пиши в комментах👇
📲 Мы в MAX
👉@BookJava@Transactional в Spring
✔️Почему важны proxy и вызов метода через Spring Bean
✔️Как работают propagation-режимы на примере REQUIRED и REQUIRES_NEW
✔️Когда происходит rollback и почему checked exceptions не всегда откатывают транзакцию
✔️Типовые ошибки при работе с транзакциями в сервисном слое
Урок будет полезен Java/Kotlin-разработчикам, которые уже пишут приложения на Spring или начинают использовать Spring в реальных backend-проектах и хотят лучше понимать поведение транзакций.
🔗 Ссылка на регистрацию: https://vk.cc/cZ9GGY
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576boolean в Java — это не просто true или false. А за этим простым типом скрывается интересный нюанс, особенно если ты задумываешься об экономии памяти.
В Java нет отдельного типа, который занимает всего 1 бит. Хотя логично было бы ожидать, что boolean — это один бит (true/false), на самом деле в памяти он занимает 1 байт (а иногда и больше, в зависимости от структуры объекта).
Пример:
public class Flags {
boolean flag1;
boolean flag2;
boolean flag3;
}
Ты думаешь — три бита. Но JVM выравнивает поля, и из-за этого объект может занимать 16 байт или больше, в зависимости от архитектуры. Почему так?
📌 Причина:
JVM упрощает модель памяти ради производительности — доступ к байтам быстрее, чем к битам. Нет битовых сдвигов, масок и лишней логики.
💡 Что делать, если хочется сэкономить память?
Используй BitSet:
BitSet flags = new BitSet(3);
flags.set(0, true);
Это уже реальная битовая структура. Отличный выбор, если у тебя десятки или сотни логических флагов.
А ты знал об этом нюансе хранения boolean? Пиши в комментариях, сталкивался ли с перерасходом памяти из-за простых типов.
📲 Мы в MAX
👉@BookJavart.jar, java.base, и так далее). На него даже нельзя получить ссылку в коде.
2. Platform ClassLoader (ранее Extension)
Загружает модули платформы (jmods), доступные из JDK, но не из java.base.
3. Application ClassLoader
Твой лучший друг. Он отвечает за загрузку классов из classpath (например, target/classes и lib/*.jar).
Но вот где начинается магия — ты можешь создать собственный ClassLoader и загружать классы в рантайме из файлов, БД или даже сети. Например:
ClassLoader customLoader = new URLClassLoader(new URL[]{new File("plugins/").toURI().toURL()});
Class<?> pluginClass = customLoader.loadClass("com.example.PluginImpl");
💡 Это используется в плагинных системах (например, IntelliJ, Jenkins, Minecraft).
Но будь осторожен — неправильная работа с загрузчиками может привести к ClassCastException, даже если классы выглядят одинаково.
📲 Мы в MAX
👉@BookJavatry-catch.
📲 Мы в MAX
👉@BookJavanullable = true в JPA-сущностях по умолчанию, без осознанного выбора.
Когда мы пишем:
@Column(name = "middle_name")
private String middleName;
JPA считает, что поле nullable, даже если по бизнес-логике оно быть пустым не должно. А вот что будет, если вы забыли это уточнить:
1. На уровне БД поле будет NULLABLE.
2. Hibernate не подскажет, что вы забыли заполнить поле.
3. В будущем это приведёт к NPE, особенно при маппинге DTO → Entity.
4. При миграциях Flyway/ Liquibase — возможно несоответствие схемы и модели.
🔍 Что делать?
1. Явно указывать nullable = false, если поле обязано быть заполнено:
@Column(name = "email", nullable = false)
private String email;
2. Использовать Bean Validation (@NotNull) — и не забывать включить её в контроллерах, сервисах, Hibernate.
3. Проверяйте соответствие схемы и сущностей. Можно использовать плагин Hibernate5DDL или включать валидацию схемы при старте.
📌 Простой совет: по умолчанию всё @Column(nullable = false), пока не докажете обратное.
Берегите свои сущности 😉
📲 Мы в MAX
👉@BookJava
log.info("User found: " + user);
Кажется безобидным? А теперь представьте, что в user лежит целый граф сущностей с ленивыми загрузками, или список из тысячи записей. Вы просто убьёте читаемость логов и производительность.
Вот что делать вместо:
if (log.isDebugEnabled()) {
log.debug("User found: {}", user);
}
А ещё лучше — логируйте только то, что действительно нужно:
log.debug("User found: id={}, email={}", user.getId(), user.getEmail());
Так вы:
- Уменьшите размер логов
- Сохраните ценную информацию
- Упростите разбор инцидентов в проде
📌 Советы:
- INFO — для бизнес-событий (например, “заказ оформлен”)
- DEBUG — для отладки
- WARN и ERROR — для проблем, которые требуют внимания
А ты проверял свои логи в проде? Не пора ли провести ревизию?
📲 Мы в MAX
👉@BookJavaJOIN, CASE, оконных функциях или CTE — протестируй это на стороне базы, как обычную функцию.
🔹 Создаём функцию в PostgreSQL:
CREATE OR REPLACE FUNCTION test_discount(user_id INT)
RETURNS NUMERIC AS $$
BEGIN
RETURN (
SELECT
CASE
WHEN u.vip = true THEN 0.2
ELSE 0.05
END
FROM users u WHERE u.id = user_id
);
END;
$$ LANGUAGE plpgsql;
🔹 Проверяем прямо в базе:
SELECT test_discount(101); -- вернёт 0.2 или 0.05
✅ Это удобно, когда:
- Ты хочешь протестировать ветки логики без запуска всего приложения;
- У тебя CI/CD запускает SQL-тесты отдельно (через pgTAP, например);
- Ты хочешь быстро показать запрос аналитику или тимлиду без Java-контекста.
💡 Лайфхак: если ты используешь Liquibase/Flyway — можно держать такие функции прямо в changelog'ах как test-only objects, не влияя на runtime-приложение.
Попробуй — экономит массу времени на ревью и отладке запросов!
📲 Мы в MAX
👉@BookJavabc-java) - это настоящий швейцарский нож для любых криптографических задач.
Стандартной реализации JCA/JCE (Java Cryptography Architecture / Extension) часто не хватает для сложных или специфических сценариев. Bouncy Castle блестяще решает эту проблему, предлагая как низкоуровневый (lightweight) криптографический API, так и полнофункциональный провайдер для JCE.
🛠 Главные возможности библиотеки:
• Огромный выбор алгоритмов: От классических RSA, AES и ECC до различных национальных стандартов (включая ГОСТы) и экзотических шифров.
• Постквантовая криптография (PQC): Библиотека идет в ногу со временем и активно внедряет постквантовые алгоритмы, устойчивые к будущим атакам с использованием квантовых компьютеров.
• Инфраструктура открытых ключей (PKI): Мощнейшие и очень удобные API для генерации и обработки X.509 сертификатов, CRL, а также работа со стандартами CMS, S/MIME, OpenPGP, TSP, CMP, CRMF и OCSP.
• Поддержка TLS/DTLS: Собственный независимый API для работы с защищенными сетевыми протоколами (вплоть до TLS 1.3).
• Независимость от версии Java: Bouncy Castle поддерживает широкий спектр версий JVM, начиная с весьма старых (J2SE 1.5+) и заканчивая самыми современными.
💡 Когда это нужно?
Bouncy Castle выручает там, где пасуют стандартные средства JDK. Нужно сгенерировать сложный сертификат «на лету», распарсить зашифрованное PGP-сообщение, использовать нестандартную эллиптическую кривую или реализовать защищенный протокол без привязки к конкретной ОС? Для всего этого используется Bouncy Castle.
https://github.com/bcgit/bc-java
📲 Мы в MAX
👉@BookJavaCollectors.collectingAndThen.
Если вы когда-нибудь писали что-то вроде:
List<String> list = someStream
.collect(Collectors.toList());
return Collections.unmodifiableList(list);
то collectingAndThen сделает это в одну строку:
List<String> list = someStream.collect(
Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
)
);
Где это может пригодиться?
– Возвращаете коллекцию из метода и не хотите, чтобы кто-то её изменял
– Хотите обернуть результат в Optional, Set, Map, EnumMap и т.д.
– Нужно после сбора в Stream ещё что-то сделать над результатом (например, отсортировать, фильтровать, обернуть)
Ещё пример:
Optional<String> maxName = people.stream()
.map(Person::getName)
.collect(Collectors.collectingAndThen(
Collectors.maxBy(Comparator.naturalOrder()),
Optional::ofNullable
));
Этот метод часто остаётся в тени, но он может существенно упростить код и сделать его чище. Попробуйте использовать его в своём проекте 😉
📲 Мы в MAX
👉@BookJava@TransactionalEventListener — это специализированная версия @EventListener, которая прослушивает событие и ждёт завершения текущей транзакции, прежде чем сработать.
Ожидание согласованного состояния базы данных позволяет безопаснее реагировать на изменения, внесённые в БД ✨
📲 Мы в MAX
👉@BookJavaCollection от List и когда что применять.
🔹 Collection — это базовый интерфейс всех коллекций в Java. Он описывает общие операции:
add(), remove(), size(), contains() и т.д.
🔹 List — это подинтерфейс Collection, предназначенный для работы с упорядоченными списками.
Дополнительно даёт методы:
get(index), set(index, value), indexOf(), add(index, value).
🔧 Пример с Collection:
Collection<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
for (String name : names) {
System.out.println(name);
}
Здесь важно только наличие элементов — порядок и индексы не важны.
🔧 Пример с List:
List<String> cities = new ArrayList<>();
cities.add("Moscow");
cities.add("Berlin");
cities.add(1, "Paris");
System.out.println(cities.get(1)); // Paris
В этом случае нужен порядок и доступ по индексу — значит, выбираем List.
📌 Когда использовать что:
- Используй Collection, если хочешь абстрагироваться от конкретной структуры и не используешь индексы.
- Используй List, если:
- важен порядок добавления,
- нужен доступ по индексу,
- требуется вставка в определённое место.
🧠 Совет:
При проектировании методов или API лучше принимать Collection — так ты не ограничиваешь пользователя в реализации.
А если внутри метода тебе нужны индексы — переходи на List.
📲 Мы в MAX
👉@BookJava