Библиотека джависта | Java, Spring, Maven, Hibernate
22.8K subscribers
2.32K photos
51 videos
47 files
3.34K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://t.me/proglibrary/9197

Для обратной связи: @proglibrary_feeedback_bot

По рекламе: @proglib_adv

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
🕯 Модели многопоточности: что выбирают разные языки

Давайте разберем три принципиально разных подхода к многопоточности и посмотрим, к чему они приводят на практике.

➡️ Модель 1: Event Loop (Python, JavaScript/Node.js)

▪️ Суть: один OS-поток на процесс. Runtime сам переключает контекст между задачами.

В CPython есть GIL (Global Interpreter Lock) — мьютекс, который позволяет выполнять байткод только одному потоку за раз. Это сделано для упрощения управления памятью через reference counting. В JavaScript модель изначально однопоточная — event loop обрабатывает задачи последовательно.

▪️Что это значит на практике

— Один инстанс сервиса = одно ядро процессора.
— Для использования 16 ядер нужно запустить 16 процессов + балансировщик.
— Потоки в Python/JS — это логическая абстракция, не настоящие OS threads.
— Для CPU-intensive задач в Python используют multiprocessing (отдельные процессы с изолированной памятью).

✔️ Плюсы

— Простой код без race conditions и deadlocks.
— Не нужны сложные примитивы синхронизации.
— Идеально для I/O-bound нагрузки (сеть, БД).

Минусы

— Усложненная инфраструктура (больше процессов, межпроцессное взаимодействие через очереди/БД).
— Процессы изолированы — нет shared memory.
— Сложнее использовать многоядерность.

➡️ Модель 2: Platform Threads (Traditional Java)

▪️ Суть: 1:1 mapping между Java Thread и OS thread. Планировщик ОС управляет всеми потоками.

Когда вы создаете new Thread() в классической Java, вы напрямую создаете OS thread. Все потоки конкурируют за CPU время через планировщик ОС. У каждого потока ~1 МБ стека.

▪️ Что это значит на практике

— Ограничение на количество потоков (~тысячи, не миллионы).
— Создание потока дорогая операция (~1ms).
— Context switch происходит в kernel space (затратно).
— Поэтому появились thread pools, чтобы переиспользовать потоки.

✔️ Плюсы

— Простая инфраструктура — один процесс использует все ядра.
— Shared memory между потоками (быстрый обмен данными).

Минусы

— Race conditions, deadlocks, visibility/atomicity проблемы.
— Многопоточный код сложен в тестировании и отладке.
— Ограничение на количество потоков может стать узким местом даже при незагруженном CPU.

➡️ Модель 3: Virtual Threads / Goroutines (Java 21+, Go)

▪️ Суть: M:N mapping. Runtime сам мультиплексирует легковесные потоки на OS threads.

В Go это goroutines (управляются Go runtime через GMP-планировщик). В Java 21+ — Virtual Threads (Project Loom). Оба подхода реализуют концепцию green threads или user-level threads.

▪️ Механизм работы

Virtual Threads поверх ForkJoinPool.

— JVM создает carrier threads (platform threads).
— Виртуальные потоки привязываются/отвязываются от carrier threads.
— При блокирующей операции виртуальный поток отвязывается, carrier thread берет другой виртуальный поток.
— Стеки virtual threads хранятся в heap, управляются GC.

▪️Что это значит на практике

— Можно создавать миллионы потоков (в Go — goroutines, в Java — виртуальные потоки).
— Создание потока почти бесплатно (байты памяти вместо мегабайт).
— Блокирующий код работает эффективно — runtime просто переключает контекст на другую задачу.
— Планировщик делает принудительное переключение, предотвращая монополизацию CPU.

✔️ Плюсы

— Нет лимита на количество одновременных задач.
— Простой императивный код вместо async/reactive.
— Shared memory все еще доступна.
— Эффективное использование ресурсов.

Минусы

— Проблемы многопоточности остаются (race conditions, deadlocks).
— В Java есть pinning проблема (synchronized блоки могут прибить virtual thread к carrier thread).
— Thread-local переменные нужно использовать осторожно.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥3🤔2
🖥 Kotlin + JPA = 💔 ?

Думаете, Kotlin идеально сочетается с JPA? А вот и нет!

data class, val, final-классы и даже дефолтные значения — всё это может тихо саботировать вашу персистентность.

ℹ️ В статье

→ Почему data-классы — плохая идея для entity
→ Как val ломает контракт JPA (но работает... пока)
→ Почему первичный ключ должен быть nullable
→ Как правильно настроить проект с плагинами no-arg и all-open

Не наступайте на грабли — читайте, пока не поздно

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍2🔥1
😮 Топ-вакансий для джавистов за неделю

Java Developer (Middle) — 200 000 —‍ 250 000 ₽ — удалёнка

Middle / Senior Java Developer — офис/гибрид (Москва)

Java Developer (Middle / Senior) — удалёнка

➡️ Еще больше топовых вакансий — в нашем канале Java jobs
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱3👍2🔥1👏1
Enterprise AI: внедряем агентов в сложные бизнес-системы

Для Java-разработчика ИИ-агенты — это новый этап автоматизации процессов. Поймите, как проектировать автономные модули, которые могут быть интегрированы в общую корпоративную архитектуру.

Курс «Углубленные AI-агенты» даст вам фундаментальную базу.

Чему мы научим:

— проектирование мультиагентных структур и связей;
— работа с актуальными фреймворками CrewAI и LangGraph;
— использование RAG-систем для работы с данными;
— оркестрация и масштабирование ИИ-решений.

Курс уже стартовал, успейте присоединиться к группе.

🚀 Записаться на основной курс

Если сомневаетесь — просто посмотрите вводное занятие.
1😁1
⚙️ Генерим Spring Boot-фичи с AI

Нужно быстро набросать сервис для бизнес-функции? AI поможет сэкономить часы на шаблонах, валидации и boilerplate.

📝 Промпт:

Generate a Spring Boot REST endpoint that handles [описание бизнес-логики]. Include request/response DTOs, validation, exception handling, and service-layer interaction. Use best practices and explain design decisions.


💡 Дополнительные возможности:

— Добавьте Use Java 17+ features if possible, чтобы сразу писать современно
— Добавьте Add unit tests for controller and service, чтобы не откладывать на потом
— Добавьте Make it modular for future feature extension, если планируется масштабирование

💬 А вы используете AI как «копилку шаблонов» или уже доверяете ему фичи под ключ?

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁42🔥1
🎯 Перестань инжектить зависимости через @Autowired на полях

Было:

@Service
public class UserService {
@Autowired
private UserRepository userRepository;

@Autowired
private EmailService emailService;

@Autowired
private ValidationService validationService;
}


✔️ Стало:

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final ValidationService validationService;
}


🔹 Почему лучше

→ Immutable зависимости — объект полностью сконфигурирован после создания
→ Тестируемость — можно создать объект без Spring контекста.
→ Явные зависимости — видно сразу, если класс раздулся (5+ зависимостей = запах)
→ Null-safety — final поля гарантируют инициализацию
→ Рефлексия не нужна — Spring использует обычный конструктор

📌 Когда field injection допустим

@Value для примитивных конфигов
→ Тестовые классы с @MockBean

Но для основной бизнес-логики — только constructor injection.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥2👏2🥱21
State Machine Pattern для AI-агентов

Джависты знают: сложная бизнес-логика требует управления состоянием. В мире LLM хаос LangChain сменился строгой структурой LangGraph. По сути, это реализация Finite State Machine, где переходы определяет нейросеть.

Зачем это в Enterprise:

— предсказуемость: вы жёстко задаёте граф переходов;
— persistence: состояние агента можно сохранить в БД (Postgres) и восстановить при падении;
— type safety: контроль структуры данных между шагами.

Курс по архитектуре агентов уже стартовал.

Записаться на курс

Сомневаетесь? Гляньте первую лекцию — там база про устройство современных агентов.
10
🎧 Что послушать — #подкаст Javaswag #84

🔹 Ведущий: Дмитрий Волыхин
🔹 Гость: Иван Лягаев
🔹 Продолжительность: 1 час 50 минут

Иван рассказывает о Scala, её применении в банковской сфере, системе эффектов, платформенных задачах и кодогенерации. Также обсуждаются переходы между языками программирования, виртуальные потоки в Java, AI-ассистенты и работа с монорепозиториями.

🔹 Ключевые темы выпуска

04:36 — Парадигмы программирования
16:00 — Система эффектов в Scala
22:38 — Scala в банке
27:21 — Типичный Scala-сервис
33:17 — HTTP библиотеки
37:13 — Трассировка и мониторинг
44:37 — Контекст
49:30 — Переходы между языками программирования
51:54 — Type Class в Scala
57:13 — Java и Kotlin
01:00:38 — Проблемы, Сообщество и Scala
01:07:41 — Доклад о виртуальных потоках в Java
01:16:34 — ТехДолг
01:21:02 — Генерация клиентов и спецификаций API
01:29:16 — Кодогенерация
01:31:06 — Монорепозитории и многорепозитории
01:35:42 — Платформенные задачи
01:38:04 — AI-ассистенты
01:45:15 — Непопулярное мнение

🔗 Слушать выпуск

🐸 Библиотека джависта

#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1👏1
🔧 Spring Boot Actuator: кастомные health indicators для мониторинга

В production приложениях часто недостаточно стандартных health checks от Spring Boot. Например, нужно проверить доступность внешнего API, очереди сообщений или специфичную бизнес-логику.

🔹 Решение

▪️ Создание custom health indicator

@Component
public class ExternalApiHealthIndicator implements HealthIndicator {

private final RestTemplate restTemplate;
private final String apiUrl;

@Override
public Health health() {
try {
ResponseEntity<String> response = restTemplate
.getForEntity(apiUrl + "/health", String.class);

if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("endpoint", apiUrl)
.withDetail("responseTime", "120ms")
.build();
}

return Health.down()
.withDetail("endpoint", apiUrl)
.withDetail("status", response.getStatusCode())
.build();

} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.withException(e)
.build();
}
}
}


▪️ Настройка в application.yml

management:
endpoint:
health:
show-details: always
health:
defaults:
enabled: true


После добавления индикатор автоматически появится в /actuator/health. Spring вызывает метод health() при каждом запросе к endpoint'у. Можно добавить кеширование через @Cacheable с TTL 30-60 секунд, чтобы снизить нагрузку на внешние системы.

⚠️ Убедитесь, что /actuator/health доступен только для систем мониторинга (Prometheus, Kubernetes liveness probe), а не публично.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71🔥1👏1
Stateful Architecture: как это работает в AI

Java-разработчики знают цену управлению состоянием. В мире AI к этому пришли только сейчас с появлением LangGraph.

Вместо хаотичных скриптов теперь строят графы, где состояние передаётся явно, как контекст в Spring. Это позволяет создавать надёжных агентов с долгосрочной памятью и транзакционной логикой выполнения задач.

На курсе разбираем эти паттерны. Даже если пишете на Java, понимание архитектуры агентов — мастхэв для интеграции AI в энтерпрайз.

Записаться на курс

Смотрите вводный урок бесплатно (про архитектуру и графы).
📌 6 принципов безопасности API

API — это не просто интерфейс, а ворота в вашу систему. Без должной защиты они становятся уязвимой точкой для атак.

🔐 Ключевые принципы безопасности API

— Использование HTTPS
— Аутентификация и авторизация
— Ограничение запросов
— Валидация входных данных
— Управление доступом на основе ролей (RBAC)
— Мониторинг и логирование

🔗 Подробнее в статье

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥1👏1
Где вы пишете свой прекрасный код? ☕️
Anonymous Poll
89%
IntelliJ IDEA
2%
Eclipse
6%
VS Code
0%
NetBeans
0%
Vim/Emacs
3%
Блокнот
😁13🤔2🔥1
🖥 На GitHub есть отличный репозиторий

useful-java-links — там собраны лучшие материалы по Java. В репозитории найдёте библиотеки, фреймворки, инструменты и образовательные материалы по разным направлениям

🔹 Отдельный акцент сделан на решениях для работы с СУБД, поисковиками, технологиями обработки больших данных и ML. Список регулярно пополняется новыми ссылками и остаётся актуальным.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥4👏1
🔐 Spring Security 6 + JWT: Полная настройка за 10 минут

Настраиваем современную аутентификацию с JWT токенами в Spring Boot 3.x.

Шаг 1: Зависимости

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>


Шаг 2: JWT Service

@Service
public class JwtService {

@Value("${jwt.secret}")
private String secretKey;

@Value("${jwt.expiration}")
private long jwtExpiration;

private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}

public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("authorities", userDetails.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.toList());

return Jwts.builder()
.claims(claims)
.subject(userDetails.getUsername())
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + jwtExpiration))
.signWith(getSigningKey())
.compact();
}

public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}

public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}

private Claims extractAllClaims(String token) {
return Jwts.parser()
.verifyWith((SecretKey) getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}

public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return username.equals(userDetails.getUsername())
&& !isTokenExpired(token);
}

private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}

private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
}
6👍4🔥1
Шаг 3: JWT Authentication Filter

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtService jwtService;
private final UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {

final String authHeader = request.getHeader("Authorization");

if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}

final String jwt = authHeader.substring(7);
final String username = jwtService.extractUsername(jwt);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);

if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);

authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);

SecurityContextHolder.getContext().setAuthentication(authToken);
}
}

filterChain.doFilter(request, response);
}
}


Шаг 4: Security Configuration

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}


Шаг 5: Application Configuration

@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {

private final UserRepository userRepository;

@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
throws Exception {
return config.getAuthenticationManager();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
👍54🔥2
Шаг 6: Auth Controller

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

private final AuthenticationManager authenticationManager;
private final JwtService jwtService;
private final UserDetailsService userDetailsService;

@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody AuthRequest request) {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.email(),
request.password()
)
);

UserDetails user = userDetailsService.loadUserByUsername(request.email());
String token = jwtService.generateToken(user);

return ResponseEntity.ok(new AuthResponse(token));
}
}


Шаг 7: application.yml

jwt:
secret: ${JWT_SECRET}
expiration: 86400000 # 24 hours

spring:
security:
filter:
dispatcher-types: REQUEST, ERROR, ASYNC


Важно: генерируйте JWT_SECRET через Jwts.SIG.HS512.key().build() и храните в переменных окружения!

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍125🔥3🥱1