Ежемесячный опрос по грейдам. Проверим, выросло ли число senior'ов.
Ваш грейд:
🔥 — Senior
👍🏼 — Middle
❤️ — Junior
😁 — Ещё учусь
Please open Telegram to view this post
VIEW IN TELEGRAM
👍35😁25❤16🔥12
volatile — это модификатор переменной, который гарантирует:
▪️
▪️
Использовать стоит, когда:
▪️
▪️
volatile не обеспечивает атомарности, поэтому для операций «чтение-модификация-запись» нужны синхронизация или атомарные типы (AtomicInteger, AtomicReference и т.п.).
#concurrency
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5
IO (Input/Output) — это
NIO (New IO) был введён в JDK 1.4 для
#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
DDD (Domain-Driven Design) — подход к проектированию, при котором структура кода отражает структуру бизнес-домена.
Основные строительные блоки
Entity —
Value Object —
Aggregate —
Domain Service —
Repository —
Domain Event —
Bounded Context —
#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3🔥1
Идемпотентность означает, что
Способы реализации:
—
—
—
—
#core
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4🔥1
Код чистый, тесты быстрые, на реальных данных — pg_stat_activity в огне 👇
📦 Задание — code review
Ручка возвращает список заказов с информацией о товарах. Работает корректно, но DBA прибежал с графиком: каждый вызов делает тысячи запросов к БД.
@Entity
public class Order {
@Id
private Long id;
private Long userId;
private LocalDateTime createdAt;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private List<OrderItem> items;
}
@Entity
public class OrderItem {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
private Integer quantity;
}
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderRepository orderRepository;
@GetMapping("/orders")
public List<OrderDto> getOrders(@RequestParam Long userId) {
List<Order> orders = orderRepository.findByUserId(userId);
return orders.stream()
.map(order -> new OrderDto(
order.getId(),
order.getItems().stream()
.map(item -> new ItemDto(
item.getProduct().getName(),
item.getQuantity()
))
.toList()
))
.toList();
}
}
▪️ Объясни
— Точную механику N+1: где и сколько раз запросы уходят в БД в этом коде.
— Почему FetchType.EAGER «решит» проблему, но создаст другую.
— Как можно решить проблему.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19❤4👍4
Текущий уровень сложности вопросов?
🔥 — Слишком просто, хочу сложнее
👍🏼 — В самый раз
❤️ — Иногда сложновато
😁 — Часто не понимаю
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤13🔥7😁1
this — это
—
—
—
—
#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤1🔥1
Event-driven architecture — это когда компоненты
// Событие:
public record UserRegistered(User user) {}
// Listener:
@Component
public class SendWelcomeEmail {
@EventListener
public void handle(UserRegistered event) {
mailer.send(event.user().email(), "Welcome!");
}
}
// Публикация события:
applicationContext.publishEvent(new UserRegistered(user));
Зачем
В Spring
— ApplicationEventPublisher для публикации
— @EventListener для подписки
— @TransactionalEventListener — если нужно дождаться коммита транзакции
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥1
@Value
@Value("${app.name}")
private String appName;
@Value("${app.timeout:30}")
private int timeout; // 30 - default значение
@Value("#{systemProperties['user.name']}")
private String userName; // SpEL выражениеПоддерживает
— property placeholders ${...}
— SpEL выражения #{...}
— значения по умолчанию через ":"
Альтернатива для группы свойств: @ConfigurationProperties.
#spring
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1🔥1
Наследование — это механизм ООП, позволяющий создавать
Наследование реализуется с помощью ключевого слова
#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1🔥1
Никаких ошибок в логах. Никаких алертов. Данные просто не сохраняются 👇
📦 Задание — code review
Сервис нотификаций: после оплаты заказа — отправить email и записать событие в БД. Оба действия независимы, сделали асинхронно.
@Service
@RequiredArgsConstructor
public class NotificationService {
private final EmailClient emailClient;
private final EventRepository eventRepository;
private final Executor taskExecutor;
public void notifyOrderPaid(Order order) {
CompletableFuture.runAsync(
() -> emailClient.sendOrderConfirmation(order),
taskExecutor
);
CompletableFuture.runAsync(
() -> {
Event event = Event.orderPaid(order.getId());
eventRepository.save(event);
},
taskExecutor
);
}
}
▪️ Объясни
— Почему исключения из runAsync полностью проглатываются и как это работает внутри.
— Чем отличается поведение runAsync от supplyAsync в контексте обработки ошибок.
— Как переписать код так, чтобы: (1) ошибки логировались, (2) один сбой не блокировал другую задачу, (3) вызывающий код мог знать об итоге.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤3🤔2👍1
Основные проблемы многопоточности:
#concurrency
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4🔥1
✅
Сохраняй событие в таблицу outbox в той же транзакции, что и бизнес-данные. Отдельный процесс читает таблицу и публикует в Kafka.
На стороне консьюмера — идемпотентность: проверяй event_id перед обработкой.
#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1🔥1
И когда использование NESTED может неожиданно упасть?
REQUIRES_NEW —
NESTED —
1. JPA + Hibernate
2. База не умеет savepoints
3. Self-invocation
// proxy не задействован → NESTED полностью игнорируется
this.nestedMethod();
#spring
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1🔥1
Self-invocation убивает прокси
Spring создаёт AOP-прокси вокруг бина. Когда ты вызываешь this.sendEmail(),
private метод — та же история.
CGLIB-прокси не может переопределить private метод. Spring
⚠️ Ещё один подводный камень
Исключения внутри @Async метода
📌 Как правильно
—
—
—
#spring
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3🔥1
Загружаешь список из N сущностей, а потом для каждой Hibernate
// Загружаем 100 заказов — получаем 101 запрос
List<Order> orders = orderRepo.findAll();
orders.forEach(o -> o.getItems().size()); // LAZY — N запросов здесь
JOIN FETCH vs @EntityGraph
JOIN FETCH — пишешь в JPQL явно:
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :s")
List<Order> findWithItems(@Param("s") Status s);Гибко, но размножается: для каждой комбинации связей нужен свой метод.
@EntityGraph — декларативно, переиспользуемо:
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByStatus(Status status);JOIN FETCH с коллекциями →
@EntityGraph → на больших данных легко
#spring
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4🔥1
Технически
Field field = obj.getClass().getDeclaredField("finalField");
field.setAccessible(true);
field.set(obj, newValue); // может не сработатьПроблемы
—
—
—
#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1👏1
Работает у одного, но ломается у другого. В логах каша из чужих данных 👇
📦 Задание — code review
Команда добавила контекст текущего пользователя в сервис через поле. Локально — всё ок.
На проде с несколькими потоками — пользователи видят чужие данные.
@Service
public class ReportService {
private User currentUser;
private ReportFilter activeFilter;
private final ReportRepository reportRepository;
public ReportService(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public void initContext(User user, ReportFilter filter) {
this.currentUser = user;
this.activeFilter = filter;
}
public List<Report> getReports() {
if (currentUser == null) {
throw new IllegalStateException("Context not initialized");
}
return reportRepository.findByUserAndFilter(
currentUser.getId(),
activeFilter
);
}
public ReportSummary getSummary() {
List<Report> reports = getReports();
return ReportSummary.calculate(reports, currentUser);
}
}
▪️ Объясни
— Какой скоуп у бина по умолчанию в Spring и почему это делает поля-состояния опасными.
— Почему synchronized над методами не является правильным решением здесь.
— Как переписать код, чтобы работало.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤3👍2👏1
Ежемесячный опрос по грейдам. Сколько среди нас
Ваш грейд:
🔥 — Senior
👍🏼 — Middle
❤️ — Junior
😁 — Ещё учусь
Please open Telegram to view this post
VIEW IN TELEGRAM
👍36🔥20😁20❤15