Spring АйО
8.43K subscribers
303 photos
209 videos
402 links
Русскоязычное сообщество Spring-разработчиков.

Habr: bit.ly/433IK46
YouTube: bit.ly/4h3Ci0x
VK: bit.ly/4hF0OG8
Rutube: bit.ly/4b4UeX6
Яндекс Музыка: bit.ly/3EIizWy

Канал для общения: @spring_aio_chat
Download Telegram
А тем временем, вышел Kotlin 2.0!

Основные изменения включают стабилизацию нового компилятора Kotlin K2, улучшение работы с многоплатформенными проектами, поддержку новых плагинов и улучшения производительности компилятора. В новой версии также прокачали smart-cast, а также ускорили работу IDE.

Несколько странно выглядит smart-cast для операций decrement и increment, меняющих тип объекта 🤯


interface Rho {
operator fun inc(): Sigma = TODO()
}

var unknownObject: Rho = TODO()

++unknownObject // после этого unknownObject: Sigma 🤪 но Kotlin знает об этом)


#Kotlin

🔗 https://kotlinlang.org/docs/whatsnew20.html

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍65👏2
This media is not supported in your browser
VIEW IN TELEGRAM
Когда меня просят стартануть проект не на Spring Boot 😂
😁13🔥3🤣31👍1
Иногда полезно освежить в памяти базовые принципы фреймворка. В Spring Framework центральное место занимает Bean. В своем видео Джош Лонг рассказывает об истории Bean и его жизненном цикле.

#SpringTips #SpringBoot

📱 https://www.youtube.com/watch?v=Z5hxolai4Tk

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥3👏2
Hibernate — очень мощный и функциональный ORM (Object-Relational Mapping) фреймворк. Он связывает базы данных с помощью объектно-ориентированных языков программирования. Однако многие, начиная с ним работать, натыкаются на проблемы производительности или отсутствия нужной функциональности. Многие из этих проблем появляются просто из-за того, что разработчики не умеют его «готовить».

🔗 https://habr.com/ru/companies/magnit/articles/814573/

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥4👌2
Когда ИИ захватит мир, он будет заставлять разработчиков писать код, чтобы давать ему все больше данных для обучения. Хотя... постойте 🤔
😁63👍1🔥1
Классная новость для всех Spring Boot разработчиков! Spring Academy дает бесплатный доступ ко всем своим обучающим материалам. 9 курсов и 12 гайдов по Spring Boot.

P.S. Есть сложности в регистрации из РФ.

🔗 https://spring.academy/

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👏3🤩2👍1
🐳 Идеальный Dockerfile для Spring Boot приложения

Контейнеризовать Spring Boot приложение можно так:

FROM gradle:8.5.0-jdk21
WORKDIR /
COPY / .
RUN ./gradlew installDist
CMD ./build/install/app/bin/app


И так:

FROM bellsoft/liberica-openjdk-alpine:21 AS builder
WORKDIR /application
COPY . .
RUN --mount=type=cache,target=/root/.gradle chmod +x gradlew && ./gradlew clean build -x test

FROM bellsoft/liberica-openjre-alpine:21 AS layers
WORKDIR /application
COPY --from=builder /application/build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM bellsoft/liberica-openjre-alpine:21
VOLUME /tmp
RUN adduser -S spring-user
USER spring-user
COPY --from=layers /application/dependencies/ ./
COPY --from=layers /application/spring-boot-loader/ ./
COPY --from=layers /application/snapshot-dependencies/ ./
COPY --from=layers /application/application/ ./

ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]


Второй вариант с одной стороны намного сложнее реализовать, но с другой стороны он даёт множество преимуществ, среди которых можно отметить:

меньший размер финального образа (из-за исключения инструментов сборки и использования JRE вместо JDK);
🛡️ более безопасный подход к работе с файловой системой (за счёт создания и использования пользователя с ограниченными правами);
🚀 ускоренный процесс сборки (благодаря кэшированию зависимостей).

🔗 Подробнее о том, как написать идеальный Dockerfile тут: https://spring.io/guides/topicals/spring-boot-docker

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍144🔥4
Spring Стартер Недели

Как сделать CRUD за 5 минут? Создаем модель данных, репозиторий, контроллер с 5-8 методами и, возможно, несколько dto. Если печатать со скоростью 600 символов в минуту, то можно успеть 🙈

Или берем Spring Data Rest, и получаем круды для своих репозиториев из коробки. И не только круды. Можно выставить наружу любой метод доступа к данным, прикрутить пагинацию и сортировку.

Однако, быстрый старт в начале может обернуться болью в процессе кастомизации или добавлении сложной (и не очень) логики. Тем не менее, выглядит как вполне себе хорошее решение для небольших REST сервисов, почти что Low Code 🙃.

Больше информации о данном стартере можно получить из доклада Рустама Курамшина на JPoint 2023.

📱 https://www.youtube.com/watch?v=roaGUHaWPxw

🔗 https://spring.io/projects/spring-data-rest

#SpringStarter #SpringBoot

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍113🔥3
This media is not supported in your browser
VIEW IN TELEGRAM
Не то чтобы, нас сильно интересовал Python, но анимация очень интересная.

Жизненный цикл кода от IDE до рабочей программы.
👍8🔥4👌3
В Kotlin 2.1 анонсировали новую интересную функцию под названием "when guards", которая расширяет возможности конструкции when. Guard'ы позволяют добавлять дополнительные проверки с помощью ключевого слова if.


fun printAnimalInfo(animal: Animal) {
when (animal) {
is Animal.Dog if animal.isFriendly -> {
println("This is a friendly dog named ${animal.name}.")
}
is Animal.Dog -> {
println("This is a dog named ${animal.name}.")
}
is Animal.Cat if animal.isLazy -> {
println("This is a lazy cat named ${animal.name}.")
}
is Animal.Cat -> {
println("This is a cat named ${animal.name}.")
}
}
}

sealed class Animal {
class Dog(val name: String, val isFriendly: Boolean) : Animal()
class Cat(val name: String, val isLazy: Boolean) : Animal()
}


Что интересно, это, кажется, первая фича языка, которую Kotlin взял из Java.


record Point(int x, int y) {
}

class Cartesian {
static void printQuadrant(Point p) {
switch (p) {
case Point(var x, var y) when x > 0 && y > 0 -> {
System.out.println("first");
}
default ->
System.out.println("other");
}
}
}


Какой вариант вам кажется более предпочтительным?

👍 - Kotlin, ❤️ - Java
18👍10
🕐 Тонкое управление Scheduled задачами в Spring

Какими способами можно настроить время и условие запуска Scheduled задач в Spring?

Первый способ - через @Profile над сервисом, в котором объявлена задача, чтобы указать профили, в которых она должна (или не должна) запускаться.


java
@Profile("test,!prod")
class Teapot {
@Scheduled(cron = "0 15 10 15 * ?")
void makeTea() {
log.info("I'am a teapot.");
}
}


Но в таком варианте, мы управляем только условием запуска, но не можем настроить переодичность.

Другой вариант, это объявление cron expression в пропертях:


#application-test.properties
teapot.make_tea.cron=0 15 10 15 * ?



@Scheduled(cron = "${teapot.make_tea.cron}")
void makeTea() {
log.info("I'am a teapot.");
}


Но как тогда в таком случае отключить джобу? В качестве cron expression можно использовать дефис(-), что означает, что джобу запускать не нужно вовсе. И тогда мы обходимся без явного перечисления профилей в @Profile.


#application-prod.properties
teapot.make_tea.cron=-


Такой подход особо полезен при использовании spring-cloud-config, тогда нет необходимости делать передеплой приложения, чтобы выключить, или донастроить джобу. Нужно только не забыть повесить @RefreshScope на класс.

#SpringBoot #SpringTips
👍10👌5🔥3
🎉 Встречайте нашу дебютную статью на Habr!

👩‍💻 Блеск и нищета нового Scrolling API в Spring Data

В статье мы разобрали новое Scrolling API, которое предназначено для эффективной пагинации. В частности, Scrolling API приносит KeySet пагинацию в Spring Data, которая в теории может быть сильно быстрее стандратной и всем известной offset пагинации.

Однако, в процессе исследования выяснилось, что с PostgreSQL новое API работает не совсем так, как ожидается. А ведь это одна из самых популярных СУБД в мире!

Заинтриговали? Переходите по ссылке, оставляйте комментарии, и, конечно, ждем ваших лайков)

🔗 https://habr.com/ru/companies/spring_aio/articles/819193/

#SpringBoot #SpringData

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍5👏4
🌟 Статическая и динамическая конфигурация в Spring Boot: что выбрать?

В мире облачных сервисов правильная настройка конфигурации играет ключевую роль. В этой статье рассматриваются два подхода: статическая и динамическая конфигурация.

🔧 Статическая конфигурация удобна предсказуемостью и простотой развертывания. Пример на Spring Boot:


@Value("${myapp.staticValue}")
private String staticValue;

public void printStaticValue() {
System.out.println(staticValue);
}


🔄 Динамическая конфигурация обеспечивает гибкость и адаптивность. Например, для изменения конфигураций во время выполнения приложения можно использовать Spring Cloud:


@RefreshScope
@RestController
public class MessageRestController {

@Value("${message:Hello default}")
private String message;

@GetMapping("/message")
public String getMessage() {
return this.message;
}
}


Spring Cloud предоставляет возможность создания config сервера, который может подгружать новые конфигурации из Git. Ознакомьтесь с полной статьей для более детального погружения и примеров кода!

📚 Читать далее

#SpringBoot #SpringCloud
🔥113👍2👏2
EqualsVerifier отмечает 15-летие 🥳

Переопределение методов equals() и hashCode() – непростая задача. Они должны соответствовать не только требованиям Java, но и используемому стеку технологий (например, JPA).

Библиотека EqualsVerifier помогает разработчикам проверить корректность переопределенных методов уже 15 лет.

Jan Ouwens, создатель библиотеки, делится своим опытом и планами в новой статье.

Узнайте, какие проблемы решались разработчиком по мере развития Java, что было популярно до GitHub, и какие планы у Jan на будущее: https://jqno.nl/post/2024/06/01/looking-back-on-15-years-of-equalsverifier/

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥53
👩‍💻 Java-рантаймы с точки зрения Spring Boot 👩‍💻

На первый взгляд, выбор правильного Java-рантайма для вашего проекта на Spring Boot может показаться тривиальным. В конце концов, все популярные рантаймы основываются на коде OpenJDK и предлагают одинаковые программные интерфейсы.

Но не все рантаймы реализованы одинаково. В этой статье мы обсудим различные показатели, которые могут повлиять на ваше решение выбрать определенный дистрибутив Java для Spring Boot приложения.

🔗 https://habr.com/ru/companies/spring_aio/articles/819899/

#JVM #Java #SpringBoot
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👏3🔥2
🐢 Неожиданное падение производительности при переходе на Spring Boot 3.3.0 👩‍💻

Hibernate подложил небольшую свинью, конвертируя JPQL в SQL. Внезапно, запрос поиска записей по списку id с передачей пустого списка деградирует в table full scan.

Однако, Hibernate пофиксил баг в течении 10 часов после поста в запрещенную в РФ соцсеть. Осталось дождаться новой версии Spring Boot, которая включит себе этот фикс.

🔗Подробнее читайте на Хабре

#SpringBoot #Hibernate #BreakingNews
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯14😱5👍3🔥2😁2
🎙 Spring АйО Чат!

Друзья, напоминаем, что у нас есть чат (@spring_aio_chat), в котором вы можете:

💬 Делиться фидбеком о публикуемых материалах;
Задавать вопросы, связанные со Spring и сопутствующими технологиями;
😼 Отвечать на вопросы, если Вы знаете на них ответ.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍4🌚2
Неблокирующие сервисы вчера: WebFlux, Coroutines и Loom.
"Неблокирующие сервисы" сегодня: DockerHub, IntelliJ IDEA Ultimate, Slack и другие.
#программисты_шутят
😁263🔥2💩2👏1
Dynamic Projection в Spring Data JPA

Используя Spring Data, довольно часто могут возникать накладные расходы из-за получения из базы слишком большого количества полей. Чтобы решить эту проблему, мы можем объявлять методы с нативными запросами, DTO или проекциями для случаев, когда необходимо ограничить количество выбираемых полей. Но такой подход приводит к появлению множества очень похожих методов в репозитории. Например:


public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByName(String name); // выбираем из базы сущность со всеми её полями

List<ProductProjection> findPrjsByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой проекции

List<ProductDto> findDtosByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой DTO
}


Чтобы решить эту проблему можно прибегнуть к использованию Dynamic Projection:


public interface ProductRepository extends JpaRepository<Product, Long> {
<T> List<T> findByName(String name, Class<T> type);
}


Подробнее о сценариях использования и компромиссах, на которые придется пойти используя Dynamic Projection, читайте в следующей статье: https://maciejwalkowiak.com/blog/spring-data-jpa-dynamic-projections/
🔥15👍11👌3
Мы рады сообщить, что наше сообщество начинает обрастать экспертами, и мы будем знакомить вас с их мнением. Встречайте, Михаил Поливаха, коммитер Spring Data JDBC (Relational), не удержался от комментария по вопросу поддержки проекций в разных модулях Spring Data. Ниже прямая речь Михаила.

Надо сказать, что projection-ы, как функциональность, должна быть доступна во всех модулях spring-data. Вопрос в том, что еще не все модули ее поддержку реализовали до конца.
Наивный читатель мог бы предположить, что поскольку в spring-data-jdbc есть поддержка как interface based projection-ов, так и dynamic projection-ов, то вот это заработает:


public interface MyEntityRepository extends CrudRepository<MyEntity, Long> {

<T> Optional<T> findProjectionByStatus(String status, Class<T> clazz);
}

@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Accessors(chain = true)
@Table
public class MyEntity {

@Id
@EqualsAndHashCode.Include
private Long id;

private String status;

@CreatedDate
private Instant createdAt;
}

interface MyEntityInterfaceProjection {
String status();

Long id();
}

@Test
void whenDynamicProjectsAreInUseViaInterfaces_thenBlast() {
saveInitialEntity();
Optional<MyEntityInterfaceProjection> active = myEntityRepository.findProjectionByStatus("active", MyEntityInterfaceProjection.class);

Assertions.assertThat(active)
.isPresent()
.hasValueSatisfying(myEntityProjection -> {
Assertions.assertThat(myEntityProjection.id() != null && myEntityProjection.status().equalsIgnoreCase("active")).isTrue();
});
}

@NotNull
private MyEntity saveInitialEntity() {
MyEntity myEntity = new MyEntity();
myEntity.setStatus("active");
return myEntityRepository.save(myEntity);
}


Но данный код сгенерит ошибку глубоко в недрах JdbcQueryCreator-a:


java.lang.IllegalStateException: SELECT does not declare a select list
at org.springframework.data.relational.core.sql.SelectValidator.doValidate(SelectValidator.java:57)
at org.springframework.data.relational.core.sql.SelectValidator.validate(SelectValidator.java:49)
at org.springframework.data.relational.core.sql.DefaultSelectBuilder.build(DefaultSelectBuilder.java:207)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:173)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:66)


Связано это с тем, что на данный момент spring-data-jdbc просто не умеет переваривать динамические проджекшены для инфтерфейсов 🙂
Про это мы знаем, бага на это дело пока нет, если он кому-либо важен - можем завести. Хочу также напомнить, что если есть какие-либо инициативы для нововведений в spring-data-jdbc - можно оставлять в комментариях или писать мне в ЛС.
🔥19👍8👏3