Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
С 28.02 по 06.03
Предыдущий пост(с 21.02 по 27.02)

Воскресный мотивационный пост:
Не было мотивации

Запись встреч/видео:
8. Логирование и ELK-стек: как расследуют инциденты в production

Обучающие статьи:
Раздел 8. Stream API и функциональный стиль

Глава 4: Искусство агрегации. Коллекторы и группировки
Коллектор — это рецепт агрегации
groupingBy и многоуровневая агрегация
(Практика): Анализ библиотеки в один проход

Советы по Java:
[Совет по Java #011]
Тема: Random — потоконебезопасный и медленный для многопоточки.

[Совет по Java #012]
Тема: При итерации по Map для получения и ключа, и значения используйте entrySet(), а не keySet() с последующим get().



Полезные статьи и видео:
Модель памяти Java процесса

Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍3🔥1
Java for Beginner
Как вам первая фаза нашего OrderHub? Нравится?

Можно выбрать много вариантов.
Как всегда все те же голосуют))

Обнял - приподнял❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 08 марта

💐 Международный женский день — праздник, который отмечается ежегодно 8 марта в ряде государств и стран мира.

Появился как день солидарности женщин в борьбе за женские права и эмансипацию. В 1945 году устав Организации Объединённых Наций стал первым международным документом, утвердившим принцип равенства мужчин и женщин. С 1977 года Международный женский день отмечается государствами-членами ООН.

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


ℹ️ Кто родился в этот день

Отто Ган (нем. Otto Hahn — Отто Хан; 8 марта 1879, Франкфурт-на-Майне — 28 июля 1968, Гёттинген) — немецкий физик, учёный-новатор в области радиохимии, открывший ядерную изомерию (Уран Z) и расщепление урана. Получил Нобелевскую премию по химии за 1944 год. Гленн Сиборг назвал его «отцом ядерной химии».

Ральф Генри Бер (англ. Ralph Henry Baer; 8 марта 1922, Родальбен, Юго-западный Пфальц — 6 декабря 2014, Манчестер, Нью-Гэмпшир) американский изобретатель, разработчик видеоигр, инженер, предприниматель, получивший прозвище «Отец видеоигр» за свой огромный вклад в развитие этой индустрии, особенно в первые годы существования компьютерных игр как таковых. В 1956 году основал собственную компанию. В ней работали до 500 инженеров, которые занимались преимущественно военными заказами. Именно эта работа подала Беру идею, которая в итоге привела к созданию первой в мире игровой приставки.

Майкл Стерн Харт (англ. Michael Stern Hart; 8 марта 1947 — 6 сентября 2011)американский писатель, изобретатель электронных книг и основатель проекта «Гутенберг», который сделал электронные книги свободно доступными через Интернет. Большинство первых материалов он напечатал и разместил лично. В 1971 году Майкл Харт получил неограниченный доступ ко времени крупного компьютера Xerox Sigma V от операторов в университете штата Иллинойс. Пытаясь достойно применить этот ресурс, он создал первую электронную книгу Декларация независимости США, когда впечатал её текст в компьютер. Так путём создания электронных копий большего количества книг получил начало Проект «Гутенберг». Избегал расходов на врачей, предпочитая домашние средства. Собрал много компьютеров и стереосистем, другой аппаратуры, часто из вышедшего из употребления оборудования, жертвуя собственной роскошью ради борьбы с безграмотностью и сохранения доступа общественности к информационным ресурсам. Вёл жизнь, близкую к бедности, «питаясь, в основном, бобовыми консервами».


🌐 Знаковые события

1950 — СССР объявил о наличии атомной бомбы.

1979 — компания Philips представляет компакт-диск.

1979 — изображения, сделанные Вояджером-I, доказали существование вулканизма на Ио, спутнике Юпитера.


#Biography #Birth_Date #Events #08марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 09 марта


ℹ️ Кто родился в этот день

Ю́рий Алексе́евич Гага́рин (9 марта 1934, Клушино, Гжатский (ныне Гагаринский) район, Западная область (ныне — Смоленская область) — 27 марта 1968, возле села Новосёлово, Киржачский район, Владимирская область) — советский космонавт и военный лётчик, первый человек, совершивший космический полёт. Герой Советского Союза, кавалер высших знаков отличия ряда государств, почётный гражданин многих российских и зарубежных городов.

Го́вард Ха́тауэй Э́йкен (англ. Howard Hathaway Aiken; 9 марта 1900, Хобокен, штат Нью-Джерси, США — 14 марта 1973, Сент-Луис, штат Миссури, США)американский пионер компьютеростроения. В должности инженера IBM руководил работами по созданию первого американского компьютера «Марк I».

Эндрю Джеймс Витерби (англ. Andrew James Viterbi, имя при рождении Андреа Джакомо Витерби (итал. Andrea Giacomo Viterbi); род. 9 марта 1935 года, Бергамо, Королевство Италия)американский инженер и бизнесмен, сооснователь Qualcomm и разработчик алгоритма Витерби.


🌐 Знаковые события

1937 — Московский телецентр на Шаболовке осуществил первую в СССР опытную передачу электронного телевидения в эфир.

1948 — на циклотроне в Калифорнии удаётся получить мезоны.


#Biography #Birth_Date #Events #09марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #013]

Тема: Аннотация @Transactional не работает на private и protected методах. Spring использует AOP-прокси, которые не могут переопределить не-public методы.

Проблема: Многие разработчики ошибочно полагают, что достаточно поставить @Transactional над любым методом, и Spring магическим образом обеспечит управление транзакциями.

Однако механизм работы Spring Transactions основан на AOP-прокси (динамических прокси JDK или CGLIB-прокси). Прокси — это подкласс или реализация интерфейса целевого класса, который переопределяет публичные методы для добавления сквозной функциональности (открытие/закрытие транзакции).

Если метод помечен как private или protected, прокси не может его переопределить, и аннотация игнорируется. Метод выполняется без транзакционного контекста, что приводит к трудноотлавливаемым багам: данные сохраняются неполностью, отсутствует откат при исключениях, соединения не закрываются.

Решение: Всегда используйте @Transactional только на public методах.

Транзакционность должна применяться на уровне сервиса (фасада), а не на внутренних вспомогательных методах. Если вам нужно вызвать транзакционный метод из того же класса, используйте self-injection (внедрение ссылки на самого себя через @Autowired/@jakarta.inject.Inject) или выделите транзакционную логику в отдельный @Service-компонент. В Spring 4.3+ можно использовать @Autowired для внедрения прокси самого себя.

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

@Autowired
private UserService selfProxy; // Self-injection для вызова из того же класса

//Антипаттерн: @Transactional на private методе
@Transactional
private void badPrivateMethod(User user) {
// Транзакции НЕ БУДЕТ! Метод не переопределяется прокси
userRepository.save(user);
if (user.getEmail() == null) {
throw new RuntimeException("Ошибка — отката не будет!");
}
}

//Правильно: public метод
@Transactional
public void goodPublicMethod(User user) {
userRepository.save(user);
if (user.getEmail() == null) {
throw new RuntimeException("Ошибка — транзакция откатится");
}
}

// Проблема внутреннего вызова
public void callInternal() {
User user = new User("John");
//Транзакции не будет — вызов напрямую, не через прокси
goodPublicMethod(user); // ФАКТИЧЕСКИ: this.goodPublicMethod()
}

//Решение: вызов через self-proxy
public void callCorrectly() {
User user = new User("John");
selfProxy.goodPublicMethod(user); // Вызов через прокси — транзакция работает
}
}

// Альтернативное решение: вынос логики в отдельный компонент
@Service
class InternalUserService {
@Transactional
public void doInTransaction(User user) {
// транзакционная логика
}
}

@Service
class MainUserService {
@Autowired
private InternalUserService internalService;

public void call() {
internalService.doInTransaction(new User("John")); // Транзакция работает
}
}


Объяснение: Spring создает прокси вокруг целевого бина.

При вызове метода через прокси сначала выполняется транзакционный перехватчик, который открывает транзакцию, затем вызывается оригинальный метод, после чего транзакция коммитится или откатывается. При вызове private метода прокси не участвует — вызов идет напрямую к целевому объекту (this). Self-injection решает эту проблему, заставляя обращаться к самому себе через прокси.

Важно понимать, что для работы @Transactional нужен публичный метод, управление транзакциями на уровне контейнера и правильная конфигурация прокси (по умолчанию — JDK dynamic proxy, требующий интерфейса, или CGLIB, работающий с классами).


#Java #советы
👍5
Что выведет код?

@SpringBootApplication
public class TransactionalTricky implements CommandLineRunner {

@Autowired
private UserService userService;

public static void main(String[] args) {
SpringApplication.run(TransactionalTricky.class, args);
}

@Override
public void run(String... args) {
userService.saveUser();
System.out.println(userService.getCounter());
}

@Service
static class UserService {
private int counter = 0;

@Transactional
public void saveUser() {
privateIncrement();
}

@Transactional
private void privateIncrement() {
counter++;
}

public int getCounter() {
return counter;
}
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
24%
0
18%
1
53%
исключение
6%
зависание
👍4
Что такое ConcurrentHashMap и чем он лучше Hashtable? 🤓

Ответ:

ConcurrentHashMap
из пакета java.util.concurrent — это потокобезопасная реализация Map, оптимизированная для многопоточной среды.

В отличие от Hashtable, которая синхронизирует все методы (блокируя всю таблицу), ConcurrentHashMap использует более тонкую блокировку.

В Java 7 — сегментирование, в Java 8+ блокируются отдельные элементы корзины (или используется Compare-And-Swap). Это позволяет параллельно выполнять чтение и запись в разные сегменты/корзины. Он также не выбрасывает ConcurrentModificationException при итерации.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 10 марта

ℹ️ Кто родился в этот день

Евге́ний Ла́заревич Роша́л (род. 10 марта 1972, Челябинск) российский программист, автор файлового менеджера FAR Manager, формата сжатия RAR, архиваторов RAR и WinRAR.


🌐 Знаковые события

1977 — несколько групп астрономов США, Австралии, Индии, ЮАР открыли кольца Урана.

1982 — редкий парад планет — все девять планет (Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран, Нептун и Плутон) собрались по одну сторону от Солнца в секторе с углом 95 градусов (то есть, максимальная разность гелиоцентрических эклиптических долгот планет составила 95 градусов).

2006 — автоматическая межпланетная станция Mars Reconnaissance Orbiter достигла Марса.


#Biography #Birth_Date #Events #10марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java

Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон

equals/hashCode в операциях distinct, groupingBy, toSet

Stream API не существует в вакууме. Он опирается на фундаментальные контракты Java-объектов, установленные классом Object: equals для логического сравнения, hashCode для хеширования, compareTo для упорядочивания.

Нарушение этих контрактов не всегда приводит к немедленным ошибкам компиляции или очевидным исключениям. Вместо этого порождаются "мистические" баги: данные исчезают, дублируются, группируются неправильно — и всё это проявляется недетерминированно, часто только под нагрузкой или при смене окружения.

Рассмотрим класс Book, разработанный без должного внимания к контрактам:
public class Book {
private final String title;
private final String author;
private final int year;

// Конструктор, геттеры
// equals и hashCode НЕ переопределены!
}


По умолчанию equals и hashCode наследуются от Object, реализуя идентичность по ссылке: два объекта равны только если это один и тот же экземпляр в памяти. Для доменной модели, где логическая эквивалентность определяется содержимым, это поведение некорректно.


groupingBy и распределение по бакетам

Коллектор groupingBy использует HashMap для организации групп. При добавлении элемента вычисляется хеш-код ключа, определяющий бакет (сегмент внутреннего массива HashMap). Если в бакете уже есть элементы, выполняется проверка equals для разрешения коллизий.

Проблема возникает при использовании ключа без корректного hashCode:
// Две логически идентичные книги
Book book1 = new Book("1984", "George Orwell", 1949);
Book book2 = new Book("1984", "George Orwell", 1949);

// Создаём поток с дублированием
List<Book> library = Arrays.asList(book1, book1, book2, book2);

// Группировка по книге как ключу
Map<Book, Long> countByBook = library.stream()
.collect(groupingBy(Function.identity(), counting()));

System.out.println(countByBook.size()); // Ожидаем 1, получаем...?


Без переопределённого hashCode book1 и book2 имеют разные хеш-коды (по умолчанию System.identityHashCode), вычисляемые из адресов памяти. Они попадают в разные бакеты HashMap. Внутри каждого бакета equals возвращает false, потому что сравнение по ссылке даёт отрицательный результат. Результат: две записи в Map вместо одной, счётчики показывают 2 для каждой "уникальной" книги.

Баг проявляется недетерминированно. При небольшом числе элементов HashMap может поместить оба объекта в один бакет случайно (из-за пересечения хешей по модулю размера таблицы), и equals обнаружит их различность, но коллизия разрешится корректно — повезло. При росте данных таблица расширяется, бакеты становятся уже, вероятность коллизии падает, баг становится стабильным. При смене JVM (разная версия, разная реализация Object.hashCode) хеш-коды меняются, и поведение меняется без изменения кода.


distinct: фильтрация дубликатов без гарантий

Операция distinct() использует LinkedHashSet для отслеживания уникальных элементов. Она полагается на тот же контракт equals/hashCode:
List<Book> uniqueBooks = library.stream()
.distinct()
.collect(toList());


Без корректных методов distinct не удаляет логические дубликаты, только ссылочные. Если book1 и book2 — разные объекты с одинаковым содержимым, оба попадут в результат. Память растёт, кэширование не работает.

Особенно опасно это в распределённых системах: объект десериализуется в разных узлах, создавая разные экземпляры в памяти. Без equals/hashCode по значению они воспринимаются как разные сущности.


toSet и потеря элементов

Collectors.toSet() возвращает HashSet, основанный на хеш-таблице. Некорректный hashCode ведёт к дублированию:
Set<Book> bookSet = library.stream()
.collect(toSet());

System.out.println(bookSet.size()); // Ожидаем 1, получаем 2


HashSet добавляет элемент, если в соответствующем бакете нет равного (по equals). Разные хеш-коды → разные бакеты → оба элемента добавлены. Множество перестаёт быть множеством.

#Java #для_новичков #beginner #stream_api #equals_hashCode
👍5
Иммутабельность ключей: бомба замедленного действия

Даже при корректных equals/hashCode опасность подстерегает в изменяемых ключах. Рассмотрим сценарий с составным ключом-автором:
public class Author {
private String name; // Не final! Сеттер есть!
private String country;

// equals и hashCode по всем полям
@Override public int hashCode() {
return Objects.hash(name, country);
}
}

// Группировка книг по автору
Map<Author, List<Book>> byAuthor = library.stream()
.collect(groupingBy(Book::author));

// Позже в коде...
Author key = byAuthor.keySet().iterator().next();
key.setName("Modified Name"); // Мутация ключа!

// Попытка доступа
List<Book> books = byAuthor.get(key); // null?!


HashMap хранит ключ в бакете, соответствующем хеш-коду на момент вставки. При изменении полей name или country hashCode объекта меняется, но позиция в таблице — нет. get(key) вычисляет новый хеш, ищет в другом бакете, не находит совпадения, возвращает null. Данные не потеряны физически (объект Author и список Book существуют в памяти), но недоступны через Map.

Это "утечка" памяти логического характера: структура данных разрастается, но поиск перестаёт работать. Баг проявляется далеко от места мутации, диагностика требует глубокого понимания работы хеш-таблиц.


Правильная реализация контрактов

Для Book корректные equals и hashCode должны учитывать все значимые поля:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return year == book.year &&
Objects.equals(title, book.title) &&
Objects.equals(author, book.author);
}

@Override
public int hashCode() {
return Objects.hash(title, author, year);
}


Ключевые требования к контракту:
Рефлексивность: x.equals(x) всегда true
Симметричность: x.equals(y) тогда и только тогда, когда y.equals(x)
Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
Консистентность: многократные вызовы дают один результат (требует иммутабельности)
Согласованность с hashCode: равные объекты имеют равные хеш-коды
Последнее требование критично для HashMap/HashSet. Нарушение (equals true, но hashCode разный) делает коллекции неработоспособными.


Рекорды как решение

Java Records (14+, стабильно в 16+) автоматически генерируют корректные equals, hashCode, toString по всем компонентам, гарантируя иммутабельность:
public record Book(String title, String author, int year) {}

// Автоматически: final поля, конструктор, геттеры, equals, hashCode, toString


Использование records для ключей groupingBy, элементов distinct, элементов toSet устраняет класс ошибок, связанных с контрактами. Единственное ограничение: компоненты records также должны быть иммутабельными и с корректными контрактами. Если Author в Book — изменяемый класс, проблема сохраняется.


Диагностика в production


При подозрении на нарушение контрактов:
Логирование хеш-кодов: вывести System.identityHashCode(obj) и obj.hashCode() для "одинаковых" объектов. Различие указывает на проблему.
Проверка equals: убедиться, что obj1.equals(obj2) возвращает true для логически эквивалентных объектов.
Анализ мутаций: установить точки останова на сеттеры ключей, используемых в Map.
Использование LinkedHashMap/LinkedHashSet: сохраняют порядок вставки, облегчая отладку.

#Java #для_новичков #beginner #stream_api #equals_hashCode
👍7
Что выведет код?

import java.util.*;
import java.util.stream.Collectors;

public class Task100326 {
public static void main(String[] args) {
List<Item100326> items = Arrays.asList(
new Item100326(1, "A"),
new Item100326(1, "B"),
new Item100326(2, "C"),
new Item100326(2, "D")
);

Set<Item100326> set = new LinkedHashSet<>(items);

Map<Integer, List<Item100326>> grouped = items.stream()
.collect(Collectors.groupingBy(Item100326::getId));

System.out.print(set.size() + " ");
System.out.print(grouped.get(1).size() + " ");
System.out.print(grouped.get(2).get(0).getName());
}

static class Item100326 {
private int id;
private String name;

Item100326(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() { return id; }
public String getName() { return name; }

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item100326 item = (Item100326) o;
return id == item.id;
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
11%
2 2 A
33%
4 2 C
44%
2 2 C
11%
4 2 A
👍3
Что такое аннотации (Annotations) и как создать свою? 🤓

Ответ:

Аннотации
— это форма метаданных, добавляемая в код (классы, методы, поля) для передачи информации компилятору, инструментам сборки или фреймворкам во время выполнения.

Они начинаются с @.

Стандартные аннотации:
@Override, @Deprecated, @SuppressWarnings.

Чтобы создать свою аннотацию, используют ключевое слово
@interface. Можно задать мета-аннотации: @Retention (когда доступна: SOURCE, CLASS, RUNTIME), @Target (к чему применять), @Inherited и др.

Аннотации могут содержать элементы (методы без параметров и тела).


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 11 марта

ℹ️ Кто родился в этот день

Джозеф Карл Робнетт Ликлайдер (англ. Joseph Carl Robnett Licklider; 11 марта 1915, Сент-Луис, штат Миссури, США — 26 июня 1990, Арлингтон, штат Массачусетс, США) известный в научной и ИТ-среде как J.C.R. или «Лик» («Lick») — американский учёный. Ранние работы были посвящены психоакустике, последующие работы — сфере информационных технологий. Один из создателей сети ARPANET, прототипа Интернета.
В 1962—1964 годы работал в ARPA, заложил основы ARPANET. Высказал идею необходимости создания объединения компьютеров в сеть со свободным доступом любого человека из любого места мира к её ресурсам. Ликлайдера называют духовным отцом всемирной сети, человеком, посеявшим семена Интернета.
Вклад Ликлайдера в возникновение Интернета огромен, он состоит из идей и принципов, а не из изобретений и технологий. Ликлайдер предвидел необходимость объединения в сеть компьютеров, имеющих простые пользовательские интерфейсы. Его идеи предвосхитили компьютерную графику, интерфейсы, работающие по принципу указания и выбора (point-and-click), цифровые библиотеки, электронную коммерцию (e-commerce), дистанционное банковское обслуживание (online banking), а также программное обеспечение, размещаемое в сети. В США его считают «Джонни Эпплсидом программирования».


Вэни́вар[a] Буш (англ. Vannevar Bush /væˈniːvɑr/, 11 марта 1890, Эверетт, Массачусетс — 28 июня 1974, Белмонт, Массачусетс)американский ученый, инженер, разработчик аналоговых компьютеров, методолог и организатор научных исследований и научного сообщества. Советник по науке при президенте Рузвельте. Автор статьи «Как мы можем мыслить», в которой предложил прообраз гипертекстового устройства Memex (интересное дальновидение - мое прим.).

Урбе́н Жан Жозе́ф Леверье́ (фр. Urbain Jean Joseph Le Verrier; 11 марта 1811, Сен-Ло — 23 сентября 1877, Париж)французский математик, занимавшийся небесной механикой, бо́льшую часть своей жизни проработавший в Парижской обсерватории. Его наиболее известным достижением является предсказание существования планеты Нептун, сделанное с помощью математического анализа астрономических наблюдений. По предложению Франсуа Араго он выполнил вычисления для объяснения несоответствий между наблюдаемой орбитой Урана и той, которая должна быть согласно законам Кеплера и Ньютона.


🌐 Знаковые события

1878 — Французской Академии продемонстрирован фонограф, но изобретение было объявлено шарлатанством.


#Biography #Birth_Date #Events #11марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #014]

Тема: Инъекция через конструктор предпочтительнее инъекции в поле. Она делает зависимости явными, поля immutable и упрощает тестирование. В новых версиях Spring @Autowired можно не ставить, если конструктор один.

Проблема: Инъекция зависимостей через поле (@Autowired над полем) — самый быстрый способ написать код, но он создает скрытые проблемы.

Поля не могут быть final, что нарушает иммутабельность и позволяет создавать объекты в частично инициализированном состоянии.
При тестировании невозможно подставить mock через конструктор — приходится использовать рефлексию (PowerMock, ReflectionTestUtils) или поднимать контекст Spring.
Также это скрывает обязательные зависимости: глядя на класс, непонятно, без каких компонентов он не может работать.

Решение: Используйте конструктор для инъекции зависимостей.

Все поля помечайте как private final — они инициализируются один раз при создании бина и гарантированно не равны null после этого.

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import java.util.Objects;

//Антипаттерн: field injection
@Service
public class BadService {

@Autowired
private UserRepository userRepository; // не final, можно забыть проинициализировать

@Autowired
private EmailService emailService; // скрытая зависимость

public void registerUser(User user) {
// может быть NullPointerException, если забыли протестировать
userRepository.save(user);
emailService.sendWelcome(user);
}

// невозможно создать объект в тесте без Spring
}

//Правильно: constructor injection
@Service
public class GoodService {

private final UserRepository userRepository;
private final EmailService emailService;
private final AuditService auditService;

// С одним конструктором @Autowired не нужен (Spring 4.3+)
public GoodService(UserRepository userRepository,
EmailService emailService,
@Lazy AuditService auditService) {
this.userRepository = Objects.requireNonNull(userRepository,
"UserRepository must not be null");
this.emailService = Objects.requireNonNull(emailService);
this.auditService = auditService; // @Lazy — прокси будет создан при первом вызове
}

public void registerUser(User user) {
userRepository.save(user);
emailService.sendWelcome(user);
auditService.log("User registered: " + user.getId());
}
}

// Для циклических зависимостей — @Lazy
@Service
class A {
private final B b;

public A(@Lazy B b) { // B создастся лениво
this.b = b;
}
}

@Service
class B {
private final A a;

public B(A a) {
this.a = a;
}
}



Объяснение: Constructor injection гарантирует, что все необходимые зависимости будут предоставлены в момент создания объекта.

Пометка полей как final защищает от случайного изменения и делает класс потокобезопасным (если сами зависимости потокобезопасны).
Проверка Objects.requireNonNull() в конструкторе обеспечивает раннее обнаружение ошибок конфигурации.

При использовании одного конструктора Spring автоматически использует его для инъекции — аннотация @Autowired становится опциональной, что соответствует принципу "convention over configuration".

Для циклических зависимостей используется @Lazy.

Разработчик всегда предпочитает constructor injection, оставляя field injection только для очень специфичных случаев (например, некоторые тестовые фреймворки).


#Java #советы
👍4
Что выведет код?

import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class Task110326 {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Task110326.class)) {
UserService110326 service = context.getBean(UserService110326.class);
System.out.println(service.getUser());
}
}
}

@Component
class UserService110326 {
private UserRepository110326 userRepository;

@Autowired
public UserService110326(UserRepository110326 userRepository) {
this.userRepository = userRepository;
}

public String getUser() {
return userRepository.findUser();
}
}

@Component
class UserRepository110326 {
private DatabaseService110326 databaseService;

@Autowired
public UserRepository110326(DatabaseService110326 databaseService) {
this.databaseService = databaseService;
}

public String findUser() {
return "User from " + databaseService.getDataSource();
}
}

@Component
class DatabaseService110326 {
public String getDataSource() {
return "MainDB";
}
}


#Tasks
👍2
Что такое SOLID принципы? 🤓

Ответ:

SOLID
— это пять основных принципов объектно-ориентированного проектирования.

S — Single Responsibility Principle (Принцип единственной ответственности): у класса должна быть только одна причина для изменения.

O — Open/Closed Principle (Открытости/закрытости): классы открыты для расширения, но закрыты для изменения.

L — Liskov Substitution Principle (Подстановки Лисков): наследники не должны нарушать поведение базового класса.

I — Interface Segregation Principle (Разделения интерфейса): много специализированных интерфейсов лучше, чем один общий.

D — Dependency Inversion Principle (Инверсии зависимостей): зависимости должны строиться на абстракциях, а не на конкретных классах.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6