Spring Boot: отдавай предпочтение статическим utility-классам вместо Spring-бинов только если они не хранят состояние, у них нет зависимостей и их не нужно мокать или оборачивать в AOP.
👉 Java Portal
public class CacheUtils {
private static Map<String, String> cache = new HashMap<>();
...
}
// Кэш как выше разделяется между всеми потоками и может случайно изменяться
@Component
@Scope("request")
public class CacheService {
private Map<String, String> cache = new HashMap<>();
...
}
// Если оформить это как бин, можно задать ему конкретный scope.
// Spring лучше управляет объектами с состоянием.Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👀3💊3❤1
Большинство разработчиков в Spring знают, как доставать query-параметры и path variables.
Но часто забывают, что HTTP-хедеры можно вытаскивать так же просто. В Spring для этого есть аккуратный
Можно в одну строку получить
Пример:
▪️ Добавляешь в метод контроллера
▪️ Spring сам подхватывает значение из заголовка
▪️ Ты получаешь его сразу параметром метода
Нужно сделать опциональным?
Просто добавь
Нужен дефолт?
▪️ Используй
▪️ Очень полезно для логирования, аналитики, A/B тестов или фичефлагов, завязанных на заголовки.
▪️ Выручает при работе с API-клиентами, которые шлют кастомные заголовки.
▪️ Чище, короче и легче тестируется.
▪️ Чем меньше зависимостей от servlet API, тем лучше.
👉 Java Portal
Но часто забывают, что HTTP-хедеры можно вытаскивать так же просто. В Spring для этого есть аккуратный
@RequestHeader.Можно в одну строку получить
User-Agent, Authorization или любой кастомный хедер. И не нужно лезть в HttpServletRequest.Пример:
@RequestHeader("User-Agent") String userAgentНужно сделать опциональным?
Просто добавь
required = false.Нужен дефолт?
defaultValue = "unknown".Please open Telegram to view this post
VIEW IN TELEGRAM
👍22
Java-совет: чтобы не ловить оверхед на boxing/unboxing, используй стримы примитивов (IntStream/LongStream/DoubleStream) вместо Stream<Integer>/Stream<Long>/Stream<Double>.
❌ Создаём объекты Integer для каждого значения:
✅ Работаем напрямую с int:
✅ Другие примитивные стримы:
👉 Java Portal
Stream<Integer> boxed = Stream.of(1, 2, 3, 4, 5);
int sumBoxed = boxed.reduce(0, Integer::sum);
int sumPrimitive = IntStream.of(1, 2, 3, 4, 5).sum();
LongStream, DoubleStreamPlease open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3🔥1
Как прогревать кэши в Spring Boot?
И вообще, как делать что-то на старте приложения?
Обычно такие операции делают в
Если “прогрев” лежит в
Покажу более элегантный способ “прогрева кэшей”. В какой-то момент точно пригодится.
Смотри:
В чём плюс?
В интеграционных тестах с
Когда всё-таки нужно, чтобы “прогрев” выполнялся и в тестах, добавь параметр “использовать main метод”:
И всё. Если код должен выполняться после старта, но мешает тестам, клади его в
👉 Java Portal
И вообще, как делать что-то на старте приложения?
Обычно такие операции делают в
@PostConstruct или ловят событие ApplicationReadyEvent. Но у этих вариантов есть заметный минус.Если “прогрев” лежит в
@PostConstruct, как его отключать в тестах? Можно завести флаг, сделать наследника и подменять бин в тестовой конфигурации, но это не всегда помогает и часто выглядит как костыль.Покажу более элегантный способ “прогрева кэшей”. В какой-то момент точно пригодится.
Смотри:
SpringApplication.run(...) возвращает полностью готовый контекст. Из него можно получить нужный компонент и вызвать метод “прогрева”. Код выглядит так:@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(MainApplication.class, args);
AccountService accService = ctx.getBean(AccountService.class);
accService.loadDictionary();
}
}
В чём плюс?
В интеграционных тестах с
@SpringBootTest метод main не запускается. Значит, код внутри не выполняется. Никаких костылей вокруг @PostConstruct, всё чисто и аккуратно.Когда всё-таки нужно, чтобы “прогрев” выполнялся и в тестах, добавь параметр “использовать main метод”:
@SpringBootTest(useMainMethod = SpringBootTest.UseMainMethod.ALWAYS)
И всё. Если код должен выполняться после старта, но мешает тестам, клади его в
main. Очень полезный приём.Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥1
Spring Boot: можно использовать
Что это делает:
▪️ Делает 3 ретрая, если вылетают указанные исключения.
▪️ Стартует с задержки 2 секунды и удваивает ее на каждой попытке (экспоненциальный backoff).
▪️ Если все попытки провалились, вызывает recover().
👉 Java Portal
@Retryable, чтобы переживать нестабильность внешних сервисов.@Service
public class ExtService {
@Retryable(
value = { HttpServerErrorException.class, ResourceAccessException.class },
maxAttempts = 3,
backoff = @Backoff(delay = 2000, multiplier = 2)
)
public ExtServiceResponse process(ExtServiceRequest request) {
// Call external payment gateway
return restTemplate.postForObject("https://ext-gateway/api/service", request,
ExtServiceResponse.class);
}
@Recover
public ExtServiceResponse recover(Exception e, ExtServiceRequest request) {
// Fallback logic after retries are exhausted
log.error("Failed after retries: {}", e.getMessage());
return new ExtServiceResponse("FAILED");
}
}
Что это делает:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥2
Вопрос для собеседования по Spring Boot:
Какой будет результат запуска, если активный профиль не указан?
❓ Spring по умолчанию выберет бин
❓ Spring выберет первый бин в алфавитном порядке
❓ Приложение упадет с
❓ Бины не будут созданы, но приложение запустится
❓ Будут созданы оба бина -
👉 Java Portal
Какой будет результат запуска, если активный профиль не указан?
DevNotificationServiceNoSuchBeanDefinitionExceptionNoUniqueBeanDefinitionExceptionPlease open Telegram to view this post
VIEW IN TELEGRAM
👍2
Java tip : старайся не шарить данные между потоками.
✅ Используй иммутабельные объекты
✅ Либо пусть потоки общаются через сообщения, не трогая общий state напрямую и не мутируя shared-состояние
👉 Java Portal
// С иммутабельными объектами:
record Book(String title, int price) {} // Иммутабельный
public class BookJob implements Runnable {
private final Book book;
.... // конструктор
@Override
public void run() {
System.out.println(book.title() + " " + book.price());
}
}
// Обмен сообщениями:
...
new Thread(() -> {
try {
queue.put("mess1");
} catch (InterruptedException e) {}
}).start();
...
new Thread(() -> {
try {
String mess = queue.take();
} catch (InterruptedException e) {}
}).start();
Please open Telegram to view this post
VIEW IN TELEGRAM
1🌚1
Какой SQL-запрос будет выполнен при вызове метода
1.
2.
3.
4.
5.
👉 Java Portal
findByEmail()?1.
SELECT * FROM users WHERE email LIKE ?2.
SELECT id, name, email FROM users WHERE email LIKE ?3.
SELECT * FROM users WHERE email = ? LIMIT 14.
SELECT * FROM users WHERE email = ?5.
SELECT * FROM users WHERE email = emailPlease open Telegram to view this post
VIEW IN TELEGRAM
Spring Boot: используй
✅ Одна из возможных причин: кастомная конфигурация конфликтует с дефолтной.
👉 Java Portal
@SpringBootApplication(exclude = …), чтобы отключить конкретные классы автоконфигурации.Please open Telegram to view this post
VIEW IN TELEGRAM
java.evolved это подборка, где каждый старый паттерн из Java показан рядом с его современным заменителем, прямо бок о бок.
👍 👍 👍
👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
java.evolved
java.evolved Code Snippets | java.evolved
A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side.
❤12🔥2👍1
Совет по Spring Boot: если тебе нужны интеграционные тесты для контроллеров Spring MVC, а всё остальное ты хочешь замокать, используй
▪️ Он грузит только MVC-бины, а не весь application context целиком.
▪️ Интеграционные тесты медленнее юнитов, поэтому их стоит ускорять, выкидывая все лишнее, что не нужно конкретно для MVC.
▪️ В этом примере мы мокaем бин
👉 Java Portal
@WebMvcTest — он поднимет только MVC-бины (контроллеры, конфиг MVC, конвертеры, валидацию и т.п.), без полного контекста приложения.HelloService и поднимаем только MVC-контекст и сам контроллер:@WebMvcTest(HelloController.class)
class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private HelloService helloService; // замокано
@Test
void sayHello_returnsExpectedMessage() throws Exception {
given(helloService.getMessage()).willReturn("Hello!");
mockMvc.perform(get("/api/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello!"));
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
Spring Boot: Используй ResponseEntity<T>, чтобы получить тонкий контроль над HTTP-ответом.
Можно явно задать status, headers и body.
👉 Java Portal
Можно явно задать status, headers и body.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Вопрос для интервью по Spring Boot:
Когда ты делаешь REST API в Spring Boot, часто используют префикс
Но добавлять
Есть ли способ проще, чтобы не писать это на каждом контроллере?
Да: можно задать единый префикс через конфигурацию.
Так ты автоматически добавляешь
👉 Java Portal
Когда ты делаешь REST API в Spring Boot, часто используют префикс
/api для всех контроллеров.Но добавлять
@RequestMapping("/api") в каждый контроллер неудобно и захламляет код.Есть ли способ проще, чтобы не писать это на каждом контроллере?
Да: можно задать единый префикс через конфигурацию.
@Configuration
class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
aClass -> aClass.getPackage().getName()
.startsWith("com.sivalabs.bookstore"));
}
}
Так ты автоматически добавляешь
/api ко всем контроллерам из нужного пакета, а код контроллеров остается чистым.Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Тестируете JBoss или другие Java application servers? 🧐
JexBoss это Python-инструмент для проверки серверов JBoss на известные уязвимости, включая проблемы с Java deserialization в ряде фреймворков (JSF, Seam, Jenkins, Struts2 и др.). Есть режим автопроверки сетей (скан диапазонов CIDR) для инвентаризации и поиска потенциально уязвимых узлов.
https://github.com/joaomatosf/jexboss
👉 Java Portal
JexBoss это Python-инструмент для проверки серверов JBoss на известные уязвимости, включая проблемы с Java deserialization в ряде фреймворков (JSF, Seam, Jenkins, Struts2 и др.). Есть режим автопроверки сетей (скан диапазонов CIDR) для инвентаризации и поиска потенциально уязвимых узлов.
https://github.com/joaomatosf/jexboss
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
Какой будет результат выполнения кода?
→
→
→
→
→ Ошибка компиляции
Примечание: по умолчанию при сериализации/десериализации Jackson опирается на спецификацию JavaBeans, то есть учитываются только методы getter и setter.
👉 Java Portal
→
{"name":"Laptop"}→
{"name":"Laptop","price":50000}→
{"price":50000}→
{}→ Ошибка компиляции
Примечание: по умолчанию при сериализации/десериализации Jackson опирается на спецификацию JavaBeans, то есть учитываются только методы getter и setter.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Продвинутый Map в Java (надо знать)
Если ты хоть раз делал
Признайся, все когда-то писали так:
Или вообще вот так:
==> В Apache Commons Collections уже давно есть готовая штука:
Что это такое
Никаких
Что умеет
- Добавление без боли:
- Массовые операции:
- Проверка существования конкретной пары:
- Удаление конкретного значения у ключа:
- Получить вообще все значения:
Реализации
-
-
👉 Java Portal
Если ты хоть раз делал
Map<String, List<String>>, этот пост для тебя.Признайся, все когда-то писали так:
Map<String, List<String>> userTags = new HashMap<>();
userTags.computeIfAbsent("user123", k -> new ArrayList<>()).add("premium");
userTags.computeIfAbsent("user123", k -> new ArrayList<>()).add("verified");
Или вообще вот так:
if (!userTags.containsKey("user123")) {
userTags.put("user123", new ArrayList<>());
}
userTags.get("user123").add("premium");==> В Apache Commons Collections уже давно есть готовая штука:
MultiValuedMap.Что это такое
MultiValuedMap<K, V> это структура данных, которая позволяет хранить несколько значений на один ключ. По сути это Map<K, Collection<V>>, но с нормальным, удобным API.MultiValuedMap<String, String> userTags = new ArrayListValuedHashMap<>();
userTags.put("user123", "premium");
userTags.put("user123", "verified");
userTags.put("user123", "early-adopter");
// Забрать все теги сразу
Collection<String> tags = userTags.get("user123");
// [premium, verified, early-adopter]
Никаких
computeIfAbsent, никаких проверок на null. Просто работает.Что умеет
- Добавление без боли:
multiMap.put("key", "value1");
multiMap.put("key", "value2"); // не затирает предыдущее значение- Массовые операции:
multiMap.putAll("user456", Arrays.asList("admin", "moderator"));- Проверка существования конкретной пары:
multiMap.containsMapping("user123", "premium"); // true/false- Удаление конкретного значения у ключа:
multiMap.removeMapping("user123", "premium");- Получить вообще все значения:
Collection<String> allTags = multiMap.values();
// все значения со всех ключей
Реализации
-
ArrayListValuedHashMap<K, V> — значения хранятся в ArrayList, порядок сохраняется, дубликаты возможны-
HashSetValuedHashMap<K, V> — значения хранятся в HashSet, без дублейPlease open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥5👀5👍2🤔1
Знания по базам данных на техсобесах игнорировать нельзя.
Честно, многие реально боятся DB. И когда бэкендер не может сходу написать обычный, часто используемый запрос, его шансы пройти начинают заметно проседать.
Один из ключевых концептов тут это JOIN.
Я собрал понятную шпаргалку по SQL JOIN: с примерами и визуализацией, чтобы быстро уложить в голове.
👉 Java Portal
Честно, многие реально боятся DB. И когда бэкендер не может сходу написать обычный, часто используемый запрос, его шансы пройти начинают заметно проседать.
Один из ключевых концептов тут это JOIN.
Я собрал понятную шпаргалку по SQL JOIN: с примерами и визуализацией, чтобы быстро уложить в голове.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍2💊1