Всем привет!
Продолжаю серию полезных видео - https://youtu.be/j-i3NQiKbcc
Тут по полочкам расписывает как работает логирование в Java.
Краткий конспект по архитектуре логирования:
1) адаптер - предоставляет API, которое вызывается из кода. На данный момент их 3 - SL4J, JCL (Apache Common Logging) и JBoss Logging. Самый распространенный и рекомендуемый - SLF4J
2) bridge - нужен, когда какая-то библиотека использует не тот адаптер, что мы хотим. По сути адаптер на адаптер, который эмулирует API, вызываемое из кода, и пробрасывает вызовы в нужный адаптер, как правило slf4j. Понятно, что когда у нас есть адаптер на адаптер, есть риск бесконечной рекурсии. Про это надо помнить)
3) движок логгера - компонента, которая пишет логи. Примеры: log4j, log4j2, logback, JUL\JDK (встроенный в JDK)
4) appender - компонент, определяющий физическое место, куда пишутся логи: консоль, диск, БД, MQ... Вот полный список для log4j2 https://logging.apache.org/log4j/2.x/manual/appenders.html
5) фильтры и конверторы - позволяют отфильтровать или преобразовать сообщения на клиенте
Плюс 3 хороших совета из видео:
1) соблюдать гигиену classpath - чистить его от лишних библиотек
2) логи в теории могут стать основой мониторинга, когда мы отбрасываем специальным образом размеченную запись в лог, которая после обработки лога становится событием мониторинга
3) не добавлять как зависимость в свои библиотеки движок логгера. Пусть его выберет потребитель, а не разбирается с вашими транзитивными зависимостями
И 2 полезные утилиты - миграторы на logback и slf4j с альтернативных библиотек логирования.
Из минусов вижу пожалуй один - там идет рассказ о связке slf4j и logback. Если во время создания видео logback сильно обогнал log4j, то сейчас с log4j2 ситуация меняется. Неплохо бы добавить сравнение с log4j2.
#java #logging
Продолжаю серию полезных видео - https://youtu.be/j-i3NQiKbcc
Тут по полочкам расписывает как работает логирование в Java.
Краткий конспект по архитектуре логирования:
1) адаптер - предоставляет API, которое вызывается из кода. На данный момент их 3 - SL4J, JCL (Apache Common Logging) и JBoss Logging. Самый распространенный и рекомендуемый - SLF4J
2) bridge - нужен, когда какая-то библиотека использует не тот адаптер, что мы хотим. По сути адаптер на адаптер, который эмулирует API, вызываемое из кода, и пробрасывает вызовы в нужный адаптер, как правило slf4j. Понятно, что когда у нас есть адаптер на адаптер, есть риск бесконечной рекурсии. Про это надо помнить)
3) движок логгера - компонента, которая пишет логи. Примеры: log4j, log4j2, logback, JUL\JDK (встроенный в JDK)
4) appender - компонент, определяющий физическое место, куда пишутся логи: консоль, диск, БД, MQ... Вот полный список для log4j2 https://logging.apache.org/log4j/2.x/manual/appenders.html
5) фильтры и конверторы - позволяют отфильтровать или преобразовать сообщения на клиенте
Плюс 3 хороших совета из видео:
1) соблюдать гигиену classpath - чистить его от лишних библиотек
2) логи в теории могут стать основой мониторинга, когда мы отбрасываем специальным образом размеченную запись в лог, которая после обработки лога становится событием мониторинга
3) не добавлять как зависимость в свои библиотеки движок логгера. Пусть его выберет потребитель, а не разбирается с вашими транзитивными зависимостями
И 2 полезные утилиты - миграторы на logback и slf4j с альтернативных библиотек логирования.
Из минусов вижу пожалуй один - там идет рассказ о связке slf4j и logback. Если во время создания видео logback сильно обогнал log4j, то сейчас с log4j2 ситуация меняется. Неплохо бы добавить сравнение с log4j2.
#java #logging
YouTube
Владимир Красильщик — Что надо знать о логировании прагматичному Java-программисту
Ближайшая конференция — JPoint 2025, 3–4 апреля (Москва + трансляция).
Подробности и билеты: https://jrg.su/T2zfbS
— —
. . . . Владимир Красильщик, Luxoft — Что надо знать о логировании прагматичному Java-программисту
Международная Java-конференция JPoint…
Подробности и билеты: https://jrg.su/T2zfbS
— —
. . . . Владимир Красильщик, Luxoft — Что надо знать о логировании прагматичному Java-программисту
Международная Java-конференция JPoint…
Всем привет!
Какие компиляторы есть в Java? Сколько их вообще?
Простой ответ - один, называется javac. Им мы компилируем исходники в байт-код, который потом исполняет JVM.
Исполняет и что важно оптимизирует.
Вообще говоря, основные оптимизации происходят именно runtime, а javac является довольно простым.
Идея в том, что собирается статистика использования кода во время выполнения, часто используемый код компилируется в "нативный" - ассемблер для конкретного процессора, а неиспользуемый - удаляется. Таким образом получаем плюс еще один компилятор - JIT, он же Just in Time. Только на самом деле, исторически, компиляторов два - один быстрый, но оптимизирующий не оптимально))), второй - наоборот, медленный и хорошо оптимизирующий. C1 и C2, сейчас они используются в паре, подробнее можно почитать тут https://for-each.dev/lessons/b/-jvm-tiered-compilation
А можно без байт-кода? Да, есть AOT - Ahead of Time компилятор, называется native-image, поставляется в GraalVM https://www.graalvm.org/latest/reference-manual/native-image/
Он сразу компилирует в требуемый "нативный" код.
А если поддержки требуемой процессорной архитектуры нет? Был момент доминирования x86, но сейчас растет популярность ARM архитектур, а там, я так понимаю, тот еще зоопарк.
А для этого уже существует промежуточный язык и набор компиляторов LLVM https://llvm.org/. Что-то типа Java байт-кода, только не привязанный к Java. GraalVM его тоже поддерживает https://www.graalvm.org/latest/reference-manual/native-image/LLVMBackend/
А можно его использовать как runtime компилятор? Почему нет, в Azul JDK решили отказаться от C1\C2 и сделать свой компилятор с блекджеком и LLVM - https://www.azul.com/products/components/falcon-jit-compiler/ Да, ошибся немного, блекджека там, увы, нет)
А еще есть компиляторы Kotlin, Scala, Groovy, Jython, JRuby... И Kotlin native, также использующий LLVM https://kotlinlang.org/docs/native-overview.html В общем я сбился со счета)
#java #jvm #jdk #native_image #compilers
Какие компиляторы есть в Java? Сколько их вообще?
Простой ответ - один, называется javac. Им мы компилируем исходники в байт-код, который потом исполняет JVM.
Исполняет и что важно оптимизирует.
Вообще говоря, основные оптимизации происходят именно runtime, а javac является довольно простым.
Идея в том, что собирается статистика использования кода во время выполнения, часто используемый код компилируется в "нативный" - ассемблер для конкретного процессора, а неиспользуемый - удаляется. Таким образом получаем плюс еще один компилятор - JIT, он же Just in Time. Только на самом деле, исторически, компиляторов два - один быстрый, но оптимизирующий не оптимально))), второй - наоборот, медленный и хорошо оптимизирующий. C1 и C2, сейчас они используются в паре, подробнее можно почитать тут https://for-each.dev/lessons/b/-jvm-tiered-compilation
А можно без байт-кода? Да, есть AOT - Ahead of Time компилятор, называется native-image, поставляется в GraalVM https://www.graalvm.org/latest/reference-manual/native-image/
Он сразу компилирует в требуемый "нативный" код.
А если поддержки требуемой процессорной архитектуры нет? Был момент доминирования x86, но сейчас растет популярность ARM архитектур, а там, я так понимаю, тот еще зоопарк.
А для этого уже существует промежуточный язык и набор компиляторов LLVM https://llvm.org/. Что-то типа Java байт-кода, только не привязанный к Java. GraalVM его тоже поддерживает https://www.graalvm.org/latest/reference-manual/native-image/LLVMBackend/
А можно его использовать как runtime компилятор? Почему нет, в Azul JDK решили отказаться от C1\C2 и сделать свой компилятор с блекджеком и LLVM - https://www.azul.com/products/components/falcon-jit-compiler/ Да, ошибся немного, блекджека там, увы, нет)
А еще есть компиляторы Kotlin, Scala, Groovy, Jython, JRuby... И Kotlin native, также использующий LLVM https://kotlinlang.org/docs/native-overview.html В общем я сбился со счета)
#java #jvm #jdk #native_image #compilers
for-each.dev
Многоуровневая компиляция в JVM | for-each.dev
Компилятор JVM Rony in Time использует несколько методов для оптимизации нашего программного обеспечения по мере его запуска.
Мы исследуем различные уровни и то, как они влияют на производительность стартапов и постоянную оптимизацию.
Мы исследуем различные уровни и то, как они влияют на производительность стартапов и постоянную оптимизацию.
Всем привет!
Продолжу серию постов https://t.me/javaKotlinDevOps/269 про оптимизацию производительности Java приложения.
В первых двух частях я говорил про такие технологии как:
1) native image - компиляция в нативный код на этапе сборки, т.об. устраняется необходимость class loading-а и JIT компиляции
2) CRaC - сохраняет и восстанавливает состояние работающего Docker образа с JRE на диск, т.об. мы получаем уже оптимизированный код
Какие еще могут быть способы выйти на оптимальную производительность побыстрее? native image мы пока отбрасываем, у нас обычная JVM и на ней запускается байт-код.
Встречный вопрос - а что мешает достижению оптимальной производительности? Как ни странно - JIT компилятор. Ведь чтобы ему понять, как оптимизировать байт-код, нужно собрать статистику. Причем процесс сбора статистики может быть цикличным - собрали, оптимизировали, поняли что оптимизация неверная, вернули байт-код обратно... И это все требует времени. А почему бы тогда не собрать статистику по использованию кода заранее, прихранить ее куда-нибудь, а потом использовать сразу со старта.
Эта техника называется Profile-Guided Optimization, в нее умеет GraalVM https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/PGO/basic-usage/ и упоминаемая ранее Azul JDK https://docs.azul.com/prime/Use-ReadyNow Но к сожалению оба - только в коммерческой версии.
Еще похожую технику использует стандартная OpenJDK при tired compilation https://for-each.dev/lessons/b/-jvm-tiered-compilation но в данном случае речь идет про отпимизацию в течение одной рабочей сессии.
P.S. Это еще не все возможные варианты, не переключайтесь)
P.P.S. Может возникнуть вопрос - зачем GraalVM использует профилирование, он же и так все оптимизировал? Нет, не все. На этапе компиляции нет информации об реальном использовании кода. А оптимизация - это не только компиляция в нативный код, это еще может быть выбрасывание лишних проверок, разворачивание цикла и т.д.
#jre #performance #java_start_boost
Продолжу серию постов https://t.me/javaKotlinDevOps/269 про оптимизацию производительности Java приложения.
В первых двух частях я говорил про такие технологии как:
1) native image - компиляция в нативный код на этапе сборки, т.об. устраняется необходимость class loading-а и JIT компиляции
2) CRaC - сохраняет и восстанавливает состояние работающего Docker образа с JRE на диск, т.об. мы получаем уже оптимизированный код
Какие еще могут быть способы выйти на оптимальную производительность побыстрее? native image мы пока отбрасываем, у нас обычная JVM и на ней запускается байт-код.
Встречный вопрос - а что мешает достижению оптимальной производительности? Как ни странно - JIT компилятор. Ведь чтобы ему понять, как оптимизировать байт-код, нужно собрать статистику. Причем процесс сбора статистики может быть цикличным - собрали, оптимизировали, поняли что оптимизация неверная, вернули байт-код обратно... И это все требует времени. А почему бы тогда не собрать статистику по использованию кода заранее, прихранить ее куда-нибудь, а потом использовать сразу со старта.
Эта техника называется Profile-Guided Optimization, в нее умеет GraalVM https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/PGO/basic-usage/ и упоминаемая ранее Azul JDK https://docs.azul.com/prime/Use-ReadyNow Но к сожалению оба - только в коммерческой версии.
Еще похожую технику использует стандартная OpenJDK при tired compilation https://for-each.dev/lessons/b/-jvm-tiered-compilation но в данном случае речь идет про отпимизацию в течение одной рабочей сессии.
P.S. Это еще не все возможные варианты, не переключайтесь)
P.P.S. Может возникнуть вопрос - зачем GraalVM использует профилирование, он же и так все оптимизировал? Нет, не все. На этапе компиляции нет информации об реальном использовании кода. А оптимизация - это не только компиляция в нативный код, это еще может быть выбрасывание лишних проверок, разворачивание цикла и т.д.
#jre #performance #java_start_boost
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.
Всем привет!
Продолжим рассказ про разные способы ускорения Java. Для начала я бы разделил ускорение в целом на 4 более конкретных направления:
1) ускорение запуска приложения за счет оптимизации\отмены первоначальной загрузки классов
2) ускорение выхода приложения на оптимальную производительность путем оптимизации JIT - Just In Time - компиляции байт-кода в нативный
3) ускорение запуска и в какой-то степени выполнения приложения за счет более легковесного фреймворка, используемого для разработки приложения
4) оптимизация сборщика мусора для достижения нужного баланса между затрачиваемыми ресурсами и паузой в обслуживании клиентских запросов, она же Stop the World
Сегодня поговорим про первое направление. С одной стороны упомянутые ранее и native image, и CRaC тоже ускоряют запуск. Но обе технологии имеют ограничения. native image запрещает reflection и динамическую загрузку классов. Образ, сохраненный с помощью CRaC, может содержать что-то лишнее, и с данной технологией нельзя просто так перезапустить приложение при сбое - т.к. возможно причина сбоя лежит в данных, подгруженные из образа.
Начну издалека.
В Java 5 появилась вот такая фича - https://docs.oracle.com/en/java/javase/21/vm/class-data-sharing.html Class-Data Sharing, сокращенно CDS.
Фича появилась и была забыта. Есть такие фичи, про которые все забывают сразу после релиза новой Java) Еще модульность из Java 9 можно вспомнить.
О чем эта фича? Мы записываем в файл метаданные загруженных классов из classpath. Потом этот файл мапился в память работающей JVM. Зачем? Цели было две:
1) расшаривание классов между несколькими инстансами JVM и т.об. уменьшение потребления RAM
2) ускорение запуска (вот оно!)
Вначале фича работала только с классами Java core. Файл с архивом классов Java core входит в состав JDK, найти его можно по имени classes.jsa. Занимает на диске сравнительно немного - 10-15 Мб. И кстати, CDS в Java включена по умолчанию, используется как раз этот файл.
Позже, в Java 10 https://openjdk.org/jeps/310 появилась возможность дампить и пользовательские классы, эту фичу назвали AppCDS. В Java 13 создание архива было упрощено https://openjdk.org/jeps/350
Пользовательские классы можно добавить в архив предварительно запустив процесс со специальной опцией командной строки -XX:ArchiveClassesAtExit
А если у нас Spring? Ребята в Spring 6.1 обратили внимание на данную опцию и добавили ключ командной строки, позволяющий собрать информацию о динамически загружаемых классах именно для Spring Boot приложения https://docs.spring.io/spring-framework/reference/integration/cds.html
А еще дали рекомендации, как максимально точно собрать информацию о классах и подтвердили, что данная опция ускоряет загрузку на ~30% https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1 Почему подтвердили - именно такую цель ставили разработчики CDS в JEP 310, упомянутом выше.
Итого - идея в чем-то похожа на Profile-Guided Optimization. Только здесь мы предварительно собираем информацию не об использовании кода, а о загруженных классах. Чем больше информации соберем - тем быстрее будет старт приложения. Минусы - версия JDK, Spring и classpath в целом должны совпадать при тестовом прогоне и использовании в ПРОМе.
#jre #performance #spring_boot #spring #java_start_boost
Продолжим рассказ про разные способы ускорения Java. Для начала я бы разделил ускорение в целом на 4 более конкретных направления:
1) ускорение запуска приложения за счет оптимизации\отмены первоначальной загрузки классов
2) ускорение выхода приложения на оптимальную производительность путем оптимизации JIT - Just In Time - компиляции байт-кода в нативный
3) ускорение запуска и в какой-то степени выполнения приложения за счет более легковесного фреймворка, используемого для разработки приложения
4) оптимизация сборщика мусора для достижения нужного баланса между затрачиваемыми ресурсами и паузой в обслуживании клиентских запросов, она же Stop the World
Сегодня поговорим про первое направление. С одной стороны упомянутые ранее и native image, и CRaC тоже ускоряют запуск. Но обе технологии имеют ограничения. native image запрещает reflection и динамическую загрузку классов. Образ, сохраненный с помощью CRaC, может содержать что-то лишнее, и с данной технологией нельзя просто так перезапустить приложение при сбое - т.к. возможно причина сбоя лежит в данных, подгруженные из образа.
Начну издалека.
В Java 5 появилась вот такая фича - https://docs.oracle.com/en/java/javase/21/vm/class-data-sharing.html Class-Data Sharing, сокращенно CDS.
Фича появилась и была забыта. Есть такие фичи, про которые все забывают сразу после релиза новой Java) Еще модульность из Java 9 можно вспомнить.
О чем эта фича? Мы записываем в файл метаданные загруженных классов из classpath. Потом этот файл мапился в память работающей JVM. Зачем? Цели было две:
1) расшаривание классов между несколькими инстансами JVM и т.об. уменьшение потребления RAM
2) ускорение запуска (вот оно!)
Вначале фича работала только с классами Java core. Файл с архивом классов Java core входит в состав JDK, найти его можно по имени classes.jsa. Занимает на диске сравнительно немного - 10-15 Мб. И кстати, CDS в Java включена по умолчанию, используется как раз этот файл.
Позже, в Java 10 https://openjdk.org/jeps/310 появилась возможность дампить и пользовательские классы, эту фичу назвали AppCDS. В Java 13 создание архива было упрощено https://openjdk.org/jeps/350
Пользовательские классы можно добавить в архив предварительно запустив процесс со специальной опцией командной строки -XX:ArchiveClassesAtExit
А если у нас Spring? Ребята в Spring 6.1 обратили внимание на данную опцию и добавили ключ командной строки, позволяющий собрать информацию о динамически загружаемых классах именно для Spring Boot приложения https://docs.spring.io/spring-framework/reference/integration/cds.html
А еще дали рекомендации, как максимально точно собрать информацию о классах и подтвердили, что данная опция ускоряет загрузку на ~30% https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1 Почему подтвердили - именно такую цель ставили разработчики CDS в JEP 310, упомянутом выше.
Итого - идея в чем-то похожа на Profile-Guided Optimization. Только здесь мы предварительно собираем информацию не об использовании кода, а о загруженных классах. Чем больше информации соберем - тем быстрее будет старт приложения. Минусы - версия JDK, Spring и classpath в целом должны совпадать при тестовом прогоне и использовании в ПРОМе.
#jre #performance #spring_boot #spring #java_start_boost
Oracle Help Center
Java Virtual Machine Guide
This chapter describes the class data sharing (CDS) feature that can help reduce the startup time and memory footprints for Java applications.
Всем привет!
Ну и еще одна оптимизация времени старта Java приложения. Самые внимательные уже могли ее заметить пройдя по ссылкам из предыдущего поста.
С момента появления Spring Boot упаковка приложения в fat jar - jar содержащий все зависимости и Tomcat в придачу (или другой контейнер сервлетов) - стала неким стандартом.
Но fat jar при исполнении требуется распаковать. А разархивация всегда требовала времени, не зря архиваторы используются как бенчмарки для процессорных тестов.
Соответственно, можно заранее разложить зависимости по отдельным файлам для ускорения старта. Вот как рекомендует это делать Spring https://docs.spring.io/spring-boot/reference/packaging/efficient.html
Судя по данным статьи из вчерашнего поста это даст еще 25% ускорения при старте https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1
#performance #spring #jvm #java_start_boost
Ну и еще одна оптимизация времени старта Java приложения. Самые внимательные уже могли ее заметить пройдя по ссылкам из предыдущего поста.
С момента появления Spring Boot упаковка приложения в fat jar - jar содержащий все зависимости и Tomcat в придачу (или другой контейнер сервлетов) - стала неким стандартом.
Но fat jar при исполнении требуется распаковать. А разархивация всегда требовала времени, не зря архиваторы используются как бенчмарки для процессорных тестов.
Соответственно, можно заранее разложить зависимости по отдельным файлам для ускорения старта. Вот как рекомендует это делать Spring https://docs.spring.io/spring-boot/reference/packaging/efficient.html
Судя по данным статьи из вчерашнего поста это даст еще 25% ускорения при старте https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1
#performance #spring #jvm #java_start_boost
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.
Всем привет!
Ну и последний вариант ускорения старта Java приложения. Самый радикальный, наверное. Отказ от Spring.
Надо отметить, что чистый hello world Spring сервис в плане старта не так уж плох, плюс минус 4 секунды. Основные проблемы начинаются с ростом числа зависимостей. И Spring можно тюнить, подробнее про это можно почитать здесь: https://www.baeldung.com/spring-boot-startup-speed Единственный момент, который мне не понравился - я бы не отключал C2 компиляцию - скорость старта может и увеличится, а вот выйти на оптимальную производительность не получится. И еще интересное исследование - https://github.com/dsyer/spring-boot-allocations Авторы выключили в Spring Boot все, за что мы его любим - Dependency Injection и быструю автоконфигурацию, повесили все на единственный classloader и ускорили старт в 5(!) раз. Только зачем нужен такой Spring?)
Но вернемся к отказу от Spring. Писать на голой Java я не предлагаю) Есть две альтернативы - Quarkus и Micronaut. Оба при создании основной целью ставили получить более быстрый и легковесный фреймворк, чем Spring.
Вот сравнительный бенчмарк Quarkus https://habr.com/ru/companies/haulmont/articles/443242/ Ускорение старта простейшего приложения в 5 раз, до 0.75 секунд. Я беру цифры без native image (GraalVM ), т.к. в этом случае и Spring будет "летать". Для интереса я сравнил локально, разница получилась не в 5 раз, а примерно в 2, с 2.5 до 1.2 секунды. За счет чего получилось ускориться можно почитать тут https://dev.to/nutrymaco/how-quarkus-use-build-time-to-start-your-application-faster-50n Если вкратце - Dependency Injection происходит во время достаточно сложного процесса компиляции.
А вот сравнение Micronaut со Spring https://www.baeldung.com/micronaut-vs-spring-boot Разница чуть поменьше, в 2,5 раза, но тоже ничего) Вот тут, авторы объясняют, почему они быстрее Spring - https://guides.micronaut.io/latest/building-a-rest-api-spring-boot-vs-micronaut-data-gradle-java.html И снова - внедрение зависимостей на этапе компиляции, нет рефлексии и создаваемых в runtime прокси.
Почему я назвал этот вариант самым тяжелым - оба фреймворка сильно отличаются от Spring - по используемым аннотациям, по API в целом. Кроме того они не такие зрелые, им порядка 5-6 лет, поэтому там просто меньше функционала.
#performance #spring #quarkus #micronaut #java_start_boost
Ну и последний вариант ускорения старта Java приложения. Самый радикальный, наверное. Отказ от Spring.
Надо отметить, что чистый hello world Spring сервис в плане старта не так уж плох, плюс минус 4 секунды. Основные проблемы начинаются с ростом числа зависимостей. И Spring можно тюнить, подробнее про это можно почитать здесь: https://www.baeldung.com/spring-boot-startup-speed Единственный момент, который мне не понравился - я бы не отключал C2 компиляцию - скорость старта может и увеличится, а вот выйти на оптимальную производительность не получится. И еще интересное исследование - https://github.com/dsyer/spring-boot-allocations Авторы выключили в Spring Boot все, за что мы его любим - Dependency Injection и быструю автоконфигурацию, повесили все на единственный classloader и ускорили старт в 5(!) раз. Только зачем нужен такой Spring?)
Но вернемся к отказу от Spring. Писать на голой Java я не предлагаю) Есть две альтернативы - Quarkus и Micronaut. Оба при создании основной целью ставили получить более быстрый и легковесный фреймворк, чем Spring.
Вот сравнительный бенчмарк Quarkus https://habr.com/ru/companies/haulmont/articles/443242/ Ускорение старта простейшего приложения в 5 раз, до 0.75 секунд. Я беру цифры без native image (GraalVM ), т.к. в этом случае и Spring будет "летать". Для интереса я сравнил локально, разница получилась не в 5 раз, а примерно в 2, с 2.5 до 1.2 секунды. За счет чего получилось ускориться можно почитать тут https://dev.to/nutrymaco/how-quarkus-use-build-time-to-start-your-application-faster-50n Если вкратце - Dependency Injection происходит во время достаточно сложного процесса компиляции.
А вот сравнение Micronaut со Spring https://www.baeldung.com/micronaut-vs-spring-boot Разница чуть поменьше, в 2,5 раза, но тоже ничего) Вот тут, авторы объясняют, почему они быстрее Spring - https://guides.micronaut.io/latest/building-a-rest-api-spring-boot-vs-micronaut-data-gradle-java.html И снова - внедрение зависимостей на этапе компиляции, нет рефлексии и создаваемых в runtime прокси.
Почему я назвал этот вариант самым тяжелым - оба фреймворка сильно отличаются от Spring - по используемым аннотациям, по API в целом. Кроме того они не такие зрелые, им порядка 5-6 лет, поэтому там просто меньше функционала.
#performance #spring #quarkus #micronaut #java_start_boost
Baeldung
Speed up Spring Boot Startup Time | Baeldung
Learn different configurations and setups that can help decrease Spring Boot startup time.
Всем привет!
И итоговый пост по оптимизации производительности Java. https://telegra.ph/Sravnenie-instrumentov-uskoreniya-Java-servisa-07-08
#java #jre #performance #comparision #java_start_boost
И итоговый пост по оптимизации производительности Java. https://telegra.ph/Sravnenie-instrumentov-uskoreniya-Java-servisa-07-08
#java #jre #performance #comparision #java_start_boost
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.
Всем привет!
Недавно я уже писал о пользе стандартизации на примере формата данных для сохранения информации о микросервисе - https://t.me/javaKotlinDevOps/291 (cyclonedx). А еще в одном посте - про работу с исключениями - упоминал библиотечку для вывода информации об ошибке в API - https://t.me/javaKotlinDevOps/310 (jdoctor).
Так вот - стандартизация добралась и сюда)
Во-первых, как выяснилось - уже с 2016 года есть стандарт "Problem Details for HTTP APIs" https://datatracker.ietf.org/doc/html/rfc7807
Во-вторых - в Spring Boot 3, т.е. с 2021 года, появилась его имплементация. Вот неплохая статья, описывающая детали https://www.baeldung.com/spring-boot-return-errors-problemdetail
Если вкратце - ответ при ошибке будет иметь стандартизированный вид похожий на такой:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Invalid request content.",
"instance": "/sales/calculate"
}
Библиотеки, решающие подобную задачу в Java уже были:
1) упомянутый ранее https://github.com/melix/jdoctor,
2) https://www.wimdeblauwe.com/blog/2022/12/01/the-error-handling-spring-boot-starter-library-vs-spring-6-problemdetail/
...
Но я думаю, что именно данное решение - от Spring, да еще по стандарту - имеет шансы распространиться достаточно широко и популяризовать данную практику.
Что еще хочется отметить:
1) стандартизация - это хорошо. Каждый поставщик API разрабатывает формат для ответа при ошибке, каждый потребитель - специфический код обработки ошибок для всех вызываемых сервисов. А при этом понятно, что формат информации об ошибке мало зависит от бизнес-процесса
2) еще в 2018 году в Сбере формат ответа для REST запроса был стандартизирован. Это круто. Не круто то, что формат отличается от RFC. И то, что область его применения была ограничена общением с фронтом
#standardization #java #problem_details
Недавно я уже писал о пользе стандартизации на примере формата данных для сохранения информации о микросервисе - https://t.me/javaKotlinDevOps/291 (cyclonedx). А еще в одном посте - про работу с исключениями - упоминал библиотечку для вывода информации об ошибке в API - https://t.me/javaKotlinDevOps/310 (jdoctor).
Так вот - стандартизация добралась и сюда)
Во-первых, как выяснилось - уже с 2016 года есть стандарт "Problem Details for HTTP APIs" https://datatracker.ietf.org/doc/html/rfc7807
Во-вторых - в Spring Boot 3, т.е. с 2021 года, появилась его имплементация. Вот неплохая статья, описывающая детали https://www.baeldung.com/spring-boot-return-errors-problemdetail
Если вкратце - ответ при ошибке будет иметь стандартизированный вид похожий на такой:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Invalid request content.",
"instance": "/sales/calculate"
}
Библиотеки, решающие подобную задачу в Java уже были:
1) упомянутый ранее https://github.com/melix/jdoctor,
2) https://www.wimdeblauwe.com/blog/2022/12/01/the-error-handling-spring-boot-starter-library-vs-spring-6-problemdetail/
...
Но я думаю, что именно данное решение - от Spring, да еще по стандарту - имеет шансы распространиться достаточно широко и популяризовать данную практику.
Что еще хочется отметить:
1) стандартизация - это хорошо. Каждый поставщик API разрабатывает формат для ответа при ошибке, каждый потребитель - специфический код обработки ошибок для всех вызываемых сервисов. А при этом понятно, что формат информации об ошибке мало зависит от бизнес-процесса
2) еще в 2018 году в Сбере формат ответа для REST запроса был стандартизирован. Это круто. Не круто то, что формат отличается от RFC. И то, что область его применения была ограничена общением с фронтом
#standardization #java #problem_details
Telegram
(java || kotlin) && devOps
Всем привет!
Если посмотреть на формат pom файлов Maven https://maven.apache.org/pom.html#POM_Reference, то можно увидеть там несколько свойств с метаданными проекта - organization, developers, contributors, scm, issueManagement... Плюс есть возможность…
Если посмотреть на формат pom файлов Maven https://maven.apache.org/pom.html#POM_Reference, то можно увидеть там несколько свойств с метаданными проекта - organization, developers, contributors, scm, issueManagement... Плюс есть возможность…
Всем привет!
Сегодня расскажу про еще один поучительный факап из моей практики.
Более 10 лет назад. Стартап. Я на данный момент и СТО, и разработчик в одном лице. Есть задача, задача в целом понятна. Декомопзирую на микросервисы и подзадачи, начинаю пилить. Дохожу до XML binding - преобразования XML в объекты и обратно. Да, тогда API в основном были XML. Решил погуглить - что сейчас есть на рынке. Кроме известного многим JAXB нахожу JiXB. Не известный тогда, и тихо умерший сейчас. Читаю описание и нахожу бенчмарк, показывающий что он ... не помню точно, но скажем в 1.5 раза быстрее JAXB. Думаю - о, круто, надо брать. Начинаю прикручивать к Spring приложению. Наталкиваюсь на кучу подводных камней - это не работает, то не работает, документации мало, ошибки не информативные и не гуглятся, т.к. сообщества особо нет. В общем убил неделю на прикручивание к проекту одной библиотеки. В итоге все заработало, конечно. А проект не был доведен до работающего состояния и так и не взлетел - как по бизнесовым причинам, так и по причине неготовности к нужному моменту.
Итоги. До сих пор стыдно за такое проектное решение. Стыдно по следующим причинам:
1) напомню, речь про стартап, т.е. нужно быстро создать работающий прототип, а не исследовать новые технологии
2) преждевременная оптимизация - данных по требуемому RPS и доступному железу на тот момент у меня не было. Нагрузочное тестирование не проводилось. Стал бы JAXB узким местом - далеко не факт
3) использовать в промышленной разработке новые, не доказавшие свою зрелость библиотеки - это риск. Очень важны сообщество (ответы на stackoverflow, а сейчас в AI чате) и работающие без бубна связки, например, Spring+конкретная технология. А риск во-первых должен быть оправдан - см. предыдущий пункт, а во-вторых - должен быть сценарий отката. Если библиотека по факту оказалась сырой - не надо заниматься реверс-инжинирингом, плодить костыли, героически превозмогать трудности. Лучше откатится на какой-то надежный вариант.
#fuckup #xml #java
Сегодня расскажу про еще один поучительный факап из моей практики.
Более 10 лет назад. Стартап. Я на данный момент и СТО, и разработчик в одном лице. Есть задача, задача в целом понятна. Декомопзирую на микросервисы и подзадачи, начинаю пилить. Дохожу до XML binding - преобразования XML в объекты и обратно. Да, тогда API в основном были XML. Решил погуглить - что сейчас есть на рынке. Кроме известного многим JAXB нахожу JiXB. Не известный тогда, и тихо умерший сейчас. Читаю описание и нахожу бенчмарк, показывающий что он ... не помню точно, но скажем в 1.5 раза быстрее JAXB. Думаю - о, круто, надо брать. Начинаю прикручивать к Spring приложению. Наталкиваюсь на кучу подводных камней - это не работает, то не работает, документации мало, ошибки не информативные и не гуглятся, т.к. сообщества особо нет. В общем убил неделю на прикручивание к проекту одной библиотеки. В итоге все заработало, конечно. А проект не был доведен до работающего состояния и так и не взлетел - как по бизнесовым причинам, так и по причине неготовности к нужному моменту.
Итоги. До сих пор стыдно за такое проектное решение. Стыдно по следующим причинам:
1) напомню, речь про стартап, т.е. нужно быстро создать работающий прототип, а не исследовать новые технологии
2) преждевременная оптимизация - данных по требуемому RPS и доступному железу на тот момент у меня не было. Нагрузочное тестирование не проводилось. Стал бы JAXB узким местом - далеко не факт
3) использовать в промышленной разработке новые, не доказавшие свою зрелость библиотеки - это риск. Очень важны сообщество (ответы на stackoverflow, а сейчас в AI чате) и работающие без бубна связки, например, Spring+конкретная технология. А риск во-первых должен быть оправдан - см. предыдущий пункт, а во-вторых - должен быть сценарий отката. Если библиотека по факту оказалась сырой - не надо заниматься реверс-инжинирингом, плодить костыли, героически превозмогать трудности. Лучше откатится на какой-то надежный вариант.
#fuckup #xml #java
Всем привет!
Давненько не было постов на канале, что поделать - конец квартала, авральный режим on.
Но я вернулся) К делу.
Java всегда славилась своим boiler plate кодом. Ремарка - конечно же не только boiler plate, но сейчас про него и про борьбу с ним. Борьба идет, и в Java 16 (а точнее в Java 14 но в режиме preview) появились records, они же записи.
Делаешь вот такое объявление:
и получаешь из коробки конструктор, getter, equals(), hashСode() и toString().
Т.е все то, что раньше приходилось писать руками или использовать Lombok. Ага, Lombok. Т.е. он стал не нужен? Не совсем. Если внимательно глянуть на список того, что под капотом делает record, то можно заметить, что там нет setter-а. Т.е. record - это иммутабельный объект и value object. Две записи с одним и тем же набором полей всегда равны. Это не баг, это фича. Маленькое дополнение - record еще меняет классический getter c getXyz на просто xyz(), но это детали. Еще дополнение - вот тут среди прочих фичей Java 17 достаточно интересно о работе с record https://habr.com/ru/companies/jugru/articles/652821/
Т.е. получается, что record заменяет @Value из Lombok.
Но не заменяет:
1) полноценный @Data - изменяемый класс без boiler plate
2) кейс, когда мы не хотим все сразу getter, toString() ... а хотим только часть этих методов
3) @Builder - тут проще показать, чем объяснять:
4) @Log - простое добавление логгера в класс
5) и наконец @SneakyThrows - если вы не любите checked exception
#java #lombok #boiler_plate_free
Давненько не было постов на канале, что поделать - конец квартала, авральный режим on.
Но я вернулся) К делу.
Java всегда славилась своим boiler plate кодом. Ремарка - конечно же не только boiler plate, но сейчас про него и про борьбу с ним. Борьба идет, и в Java 16 (а точнее в Java 14 но в режиме preview) появились records, они же записи.
Делаешь вот такое объявление:
public record Point(int x, int y) {}
и получаешь из коробки конструктор, getter, equals(), hashСode() и toString().
Т.е все то, что раньше приходилось писать руками или использовать Lombok. Ага, Lombok. Т.е. он стал не нужен? Не совсем. Если внимательно глянуть на список того, что под капотом делает record, то можно заметить, что там нет setter-а. Т.е. record - это иммутабельный объект и value object. Две записи с одним и тем же набором полей всегда равны. Это не баг, это фича. Маленькое дополнение - record еще меняет классический getter c getXyz на просто xyz(), но это детали. Еще дополнение - вот тут среди прочих фичей Java 17 достаточно интересно о работе с record https://habr.com/ru/companies/jugru/articles/652821/
Т.е. получается, что record заменяет @Value из Lombok.
Но не заменяет:
1) полноценный @Data - изменяемый класс без boiler plate
2) кейс, когда мы не хотим все сразу getter, toString() ... а хотим только часть этих методов
3) @Builder - тут проще показать, чем объяснять:
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.build();
4) @Log - простое добавление логгера в класс
5) и наконец @SneakyThrows - если вы не любите checked exception
#java #lombok #boiler_plate_free
Хабр
Java 17 для тех, кто не следил. Часть 1
Уже вышла Java 18, но для всех, кто сидит на LTS, по-прежнему остаётся актуальной версия 17. Такие люди могут не отслеживать постоянно фичи каждой новой версии, а спокойно заниматься своими делами и...
Небольшое дополнение по Java records. Как известно, Java - это огромное количество библиотек и фреймворков и, следовательно, вопросы совместимости.
Так вот. Spring Boot Web записи поддерживает, как в контроллере, так и в http клиентах. Java сериализация естественно, это же часть спецификации Java. Jackson сериализация. Mapstruct. И Spring Data JPA, там где это возможно, учитывая иммутабельность https://www.baeldung.com/spring-jpa-java-records
Что я забыл? Наверное забыл, но 3 года прошло - поддержку должны были уже запилить)
#java #spring
Так вот. Spring Boot Web записи поддерживает, как в контроллере, так и в http клиентах. Java сериализация естественно, это же часть спецификации Java. Jackson сериализация. Mapstruct. И Spring Data JPA, там где это возможно, учитывая иммутабельность https://www.baeldung.com/spring-jpa-java-records
Что я забыл? Наверное забыл, но 3 года прошло - поддержку должны были уже запилить)
#java #spring
Baeldung
Using Java Records with JPA | Baeldung
Learn how we can use records with JPA and Spring Data JPA.
Всем привет!
Давно не писал про Kotlin, а в названии канала он есть на почетном втором месте) Исправляюсь.
Основная фишка Kotlin - это упрощение написания и чтения кода за счет упрощения языка. В чем упрощение: все типы - объектные, функция всегда возвращает результат, нет неявных преобразований, нет проверяемых исключений, некоторые стандартные паттерны (синглтон, делегат) стали частью языка - не нужно изобретать велосипед. Возможность переопределения операций и полноценные функциональные типы - на самом деле тоже. Операция - краткий общеупотребительный вариант метода, функцию можно передавать как объект не создавая для этого объект.
Но как всегда есть нюансы.
Вот например inline методы и связанный с ним reified https://www.baeldung.com/kotlin/reified-functions
При беглом знакомстве возникают 2 вопроса:
1) разработчики Kotlin загрязняют язык, ведь компилятор, а скорее JVM, сами справятся с inline?
2) Kotlin хакнул type erasure для generic?
Ответ на оба вопроса - нет.
И есть отличная статья на эту тему https://habr.com/ru/articles/775120/ Автора знаю лично, рекомендую почитать эту и другие его статьи про Kotlin.
Для ленивых ))) ответы:
1) inline нужен только для методов с параметрами функционального типа, чтобы избежать обвертывания функции в объект. Java компилятор не умеет работать с функциональными типами, увы
2) reified не нарушает спецификации Java, компилятор Kotlin лишь сохраняет тип там, где он его знает, и это касается только inline методов
И про простоту Kotlin в целом и сложность inline. Как выглядит процесс со стороны:
1) у нас полноценные функциональные типы
2) в коде их будет много
3) Java не умеет с ними работать
4) сделаем inline, чтобы не снизить производительность при работе с такими типами
5) появляются баги из-за inline, приходится вводить ключевые слова noinline и crossinline. Подробнее об этом есть в статье выше.
6) кто-то просит: раз при inline мы знаем исходный тип generic - давайте дадим возможность работы с ним, появляется reified
7) возникают новые баги, их фиксят
...
Процесс вымышленный, возможно, в реальности было по-другому. Я хотел подчеркнуть вот что: одна фича тянет за собой другую, другая - несколько особых случаев. И все это усложняет язык, хотя цель была противоположная.
P.S. Ну и да, получается, во всем виновата Java)
#kotlin #java
Давно не писал про Kotlin, а в названии канала он есть на почетном втором месте) Исправляюсь.
Основная фишка Kotlin - это упрощение написания и чтения кода за счет упрощения языка. В чем упрощение: все типы - объектные, функция всегда возвращает результат, нет неявных преобразований, нет проверяемых исключений, некоторые стандартные паттерны (синглтон, делегат) стали частью языка - не нужно изобретать велосипед. Возможность переопределения операций и полноценные функциональные типы - на самом деле тоже. Операция - краткий общеупотребительный вариант метода, функцию можно передавать как объект не создавая для этого объект.
Но как всегда есть нюансы.
Вот например inline методы и связанный с ним reified https://www.baeldung.com/kotlin/reified-functions
При беглом знакомстве возникают 2 вопроса:
1) разработчики Kotlin загрязняют язык, ведь компилятор, а скорее JVM, сами справятся с inline?
2) Kotlin хакнул type erasure для generic?
Ответ на оба вопроса - нет.
И есть отличная статья на эту тему https://habr.com/ru/articles/775120/ Автора знаю лично, рекомендую почитать эту и другие его статьи про Kotlin.
Для ленивых ))) ответы:
1) inline нужен только для методов с параметрами функционального типа, чтобы избежать обвертывания функции в объект. Java компилятор не умеет работать с функциональными типами, увы
2) reified не нарушает спецификации Java, компилятор Kotlin лишь сохраняет тип там, где он его знает, и это касается только inline методов
И про простоту Kotlin в целом и сложность inline. Как выглядит процесс со стороны:
1) у нас полноценные функциональные типы
2) в коде их будет много
3) Java не умеет с ними работать
4) сделаем inline, чтобы не снизить производительность при работе с такими типами
5) появляются баги из-за inline, приходится вводить ключевые слова noinline и crossinline. Подробнее об этом есть в статье выше.
6) кто-то просит: раз при inline мы знаем исходный тип generic - давайте дадим возможность работы с ним, появляется reified
7) возникают новые баги, их фиксят
...
Процесс вымышленный, возможно, в реальности было по-другому. Я хотел подчеркнуть вот что: одна фича тянет за собой другую, другая - несколько особых случаев. И все это усложняет язык, хотя цель была противоположная.
P.S. Ну и да, получается, во всем виновата Java)
#kotlin #java
Baeldung on Kotlin
Reified Functions in Kotlin | Baeldung on Kotlin
Learn how to use reified functions in Kotlin
Всем привет!
Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #dev_compromises и #arch_compromises. Следствие этого подхода, принцип, который я бы назвал - "не все так однозначно".
Вопрос - как вы относитесь к рефлексии в Java?
Досрочный ответ: рефлексия - это плохо, лучше не использовать. Если дошло до рефлексии - значит в архитектуре проблема.
Чтобы лучше разобраться в теме надо ответить на вопрос: а почему плохо?
Ответа два:
1) рефлексия позволяет нарушить принципы ООП и, следовательно, архитектуру приложения. Автор скрыл внутренности класса через private, а мы туда лезем своими "грязными руками")))
2) снижение производительности. Тут частично работает тот факт, что делаются лишние вызовы кода. Но самое главное - JIT компилятор плохо умеет оптимизировать такой код, т.к. он слишком динамический. Изменится может сам класс, который приходит на вход метода с рефлексией
Окей, инкапсуляция нарушается, код работает медленно. Не используем?
А что с поиском аннотаций по коду? Не своих - до них мы еще дойдем - чужих, чтобы получить некие метаданные об объекте. Большинство вариантов вот тут основано на рефлексии https://www.baeldung.com/java-scan-annotations-runtime
Или у нас есть аннотации, созданные с помощью Spring AOP - это проще, чем AspectJ, если у вас используется Spring. А Spring AOP использует динамические прокси, создаваемые в runtime https://www.baeldung.com/spring-aop-vs-aspectj А с помощью чего создается прокси в runtime - правильно, рефлексии.
Да что там AOP - создание бинов из @Configuration - это тоже вызов методов @Bean через рефлексию.
Почему же рефлексию используют и предлагают к использованию, если это такая проблемная технология?
Вернемся к двум ее недостаткам:
1) не надо использовать рефлексию для вызова private методов или доступа к private полям. Если такая задача встала - у вас в самом деле проблемы с архитектурой
2) не надо использовать рефлексию часто и при этом в высоконагруженных приложениях. Тот же Spring использует рефлексию только при старте приложения для инициализации прокси и бинов. И к слову в т.ч. и поэтому старт Spring приложения может быть долгим, и люди с этим борются, см. мой цикл статей про ускорение старта #java_start_boost Более того, разработчикам Spring для поддержки native image пришлось серьезно допиливать его в т.ч. из-за динамических прокси и @Configuration https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
Итого: рефлексию стоит рассматривать как возможность получить метаданные о классе. Помня при этом о производительности.
И если вопрос упирается в производительность - всегда есть альтернативы рефлексии. Это работа с байт-кодом не в runtime:
1) compile time (свой компилятор, см. статью про AspectJ)
2) post-compile time (свой плагин для сборки, см. Jandex для поиска аннотаций https://smallrye.io/jandex/jandex/3.2.2/index.html)
3) load-time (свой агент+classloader, см. статью про AspectJ)
Все варианты сложнее в реализации и подключении к проекту, зато вносят минимальное влияние в runtime.
P.S. Да, если при упоминании о динамических прокси вы вспомнили про задачку с собесов о вложенном @Transactional - это оно. И ответ на этот вопрос не так очевиден https://habr.com/ru/articles/347752/
#java #reflection #dev_compromises
Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #dev_compromises и #arch_compromises. Следствие этого подхода, принцип, который я бы назвал - "не все так однозначно".
Вопрос - как вы относитесь к рефлексии в Java?
Досрочный ответ: рефлексия - это плохо, лучше не использовать. Если дошло до рефлексии - значит в архитектуре проблема.
Чтобы лучше разобраться в теме надо ответить на вопрос: а почему плохо?
Ответа два:
1) рефлексия позволяет нарушить принципы ООП и, следовательно, архитектуру приложения. Автор скрыл внутренности класса через private, а мы туда лезем своими "грязными руками")))
2) снижение производительности. Тут частично работает тот факт, что делаются лишние вызовы кода. Но самое главное - JIT компилятор плохо умеет оптимизировать такой код, т.к. он слишком динамический. Изменится может сам класс, который приходит на вход метода с рефлексией
Окей, инкапсуляция нарушается, код работает медленно. Не используем?
А что с поиском аннотаций по коду? Не своих - до них мы еще дойдем - чужих, чтобы получить некие метаданные об объекте. Большинство вариантов вот тут основано на рефлексии https://www.baeldung.com/java-scan-annotations-runtime
Или у нас есть аннотации, созданные с помощью Spring AOP - это проще, чем AspectJ, если у вас используется Spring. А Spring AOP использует динамические прокси, создаваемые в runtime https://www.baeldung.com/spring-aop-vs-aspectj А с помощью чего создается прокси в runtime - правильно, рефлексии.
Да что там AOP - создание бинов из @Configuration - это тоже вызов методов @Bean через рефлексию.
Почему же рефлексию используют и предлагают к использованию, если это такая проблемная технология?
Вернемся к двум ее недостаткам:
1) не надо использовать рефлексию для вызова private методов или доступа к private полям. Если такая задача встала - у вас в самом деле проблемы с архитектурой
2) не надо использовать рефлексию часто и при этом в высоконагруженных приложениях. Тот же Spring использует рефлексию только при старте приложения для инициализации прокси и бинов. И к слову в т.ч. и поэтому старт Spring приложения может быть долгим, и люди с этим борются, см. мой цикл статей про ускорение старта #java_start_boost Более того, разработчикам Spring для поддержки native image пришлось серьезно допиливать его в т.ч. из-за динамических прокси и @Configuration https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
Итого: рефлексию стоит рассматривать как возможность получить метаданные о классе. Помня при этом о производительности.
И если вопрос упирается в производительность - всегда есть альтернативы рефлексии. Это работа с байт-кодом не в runtime:
1) compile time (свой компилятор, см. статью про AspectJ)
2) post-compile time (свой плагин для сборки, см. Jandex для поиска аннотаций https://smallrye.io/jandex/jandex/3.2.2/index.html)
3) load-time (свой агент+classloader, см. статью про AspectJ)
Все варианты сложнее в реализации и подключении к проекту, зато вносят минимальное влияние в runtime.
P.S. Да, если при упоминании о динамических прокси вы вспомнили про задачку с собесов о вложенном @Transactional - это оно. И ответ на этот вопрос не так очевиден https://habr.com/ru/articles/347752/
#java #reflection #dev_compromises
Baeldung
Scanning Java Annotations at Runtime | Baeldung
Learn about scanning Java annotations at runtime.
Всем привет!
Давно хотел написать небольшую памятку по уровням логирования.
Стандартный набор уровней логирования для бизнес приложений:
TRACE
DEBUG
INFO
WARNING
ERROR
Встречается еще один - FATAL - но на моей практике это что-то разряда OutOfMemory, которое прикладной код не выбрасывает.
TRACE - самый редко встречающийся уровень. Почему? Потому что всегда, когда стоит выбор между трассировкой через логи или запуском в режиме отладчика - нужно выбирать второе. Это банально быстрее, т.к. не требует пересборок. Почему именно пересборки, во множественном числе - потому что у нас ООП, куча классов и методов, с первого раза расставить TRACE правильно вряд ли получится. Единственный минус отладки - если это происходит на тестовом или dev стенде - другие люди не смогут там работать. Т.е. их нужно предупредить. Или еще лучше - иметь минимум 2 подходящих стенда. Когда стенд не подходящий - это ПРОМ (PROD) или PROD-like стенды, куда нет сетевого доступа с компьютера разработчика. Вот это пожалуй единственное место, где может понадобиться TRACE. И если он там понадобился, то возможно у вас проблемы либо на этапе тестирования. Либо какие-то значимые отличия в конфигурации стендов, что тоже может быть поводом задуматься.
Что делать с TRACE - убирать сразу после нахождения проблемы, т.е. в следующем хотфиксе.
DEBUG - используется как правило на тестовых стендах чтобы разобраться с пришедшими от смежников данными и как эти данные влияют на состояние нашего сервиса. Разработка и отладка идет на заглушках, что там придет от смежников до конца не ясно, несмотря на согласованную аналитику - вот этот вот кейс. Если используется для трассировки шагов - см. абзац выше)
Что делать с DEBUG - убирать перед фиксацией ПРОМ релиза. Почему бы не оставить? Даже не из-за производительности, этот момент решить можно, а из-за ухудшения читаемости. Размер кода увеличивается, лог как правило дублирует близлежащий метод в плане доносимой информации, т.е. нарушаем DRY. Если DEBUG во внешней библиотеке - отключаем через настройку уровня логирования для конкретного пакета.
Все уровни далее по большому счету пишутся для сопровождения и тестировщиков.
INFO - нет ошибки, но сервис дошел до некой важной точки, и эта информация поможет при разборе полетов в случае ошибок и сбоев. Я видел кейсы, когда сопровождение запрещало писать на ПРОМ с уровнем INFO, но со временем оно одумывалось, и запрет снимали).
WARN - ошибки, не позволяющей вернуть данные клиенту, нет. Но есть либо некритичная ошибка, либо отсутствуют какие-то данные. И поэтому мы либо уходим в альтернативную ветку пользовательского сценария, либо берем данные из кэша, и возвращаем ответ клиенту.
ERROR - ошибка, прокидываемая клиенту. Не в виде stack trace конечно) Суть в том, что процесс обработки клиентского запроса прерывается, возвращается ошибка согласно API. Две самые частые ошибки, что я здесь вижу:
а) вывод error на любую ошибку смежника, даже не блокирующую.
б) error из используемой библиотеки, которая для нашего процесса не является блокирующей. В этом случае ее нужно убирать либо через настройку уровня логирования для пакета этой библиотеки, либо через ее доработку (если это возможно).
Эти три уровня убирать перед релизом не нужно, но стоит периодически их просматривать на предмет актуальности.
И последнее - по производительности. Просадка в производительности из-за логирования может быть из-за вычисления параметров даже в том случае, когда текущий уровень логирования выключен. Спасает ленивое вычисление параметров для выводимого в лог сообщения.
Это поддерживается в
а) log4j https://logging.apache.org/log4j/2.12.x/manual/api.html
б) slf4j https://www.slf4j.org/faq.html#logging_performance
через lambda параметры начиная с Java 8. Т.е. в большинстве инсталляций. Ну я надеюсь на это)
Еще просадка может быть из-за количества сообщений. Тогда смотри абзацы про TRACE и DEBUG выше. Еще можно глянуть мой пост про производительность в log4j https://t.me/javaKotlinDevOps/77 и поднять уровень логирования для ПРОМ.
#java #logging
Давно хотел написать небольшую памятку по уровням логирования.
Стандартный набор уровней логирования для бизнес приложений:
TRACE
DEBUG
INFO
WARNING
ERROR
Встречается еще один - FATAL - но на моей практике это что-то разряда OutOfMemory, которое прикладной код не выбрасывает.
TRACE - самый редко встречающийся уровень. Почему? Потому что всегда, когда стоит выбор между трассировкой через логи или запуском в режиме отладчика - нужно выбирать второе. Это банально быстрее, т.к. не требует пересборок. Почему именно пересборки, во множественном числе - потому что у нас ООП, куча классов и методов, с первого раза расставить TRACE правильно вряд ли получится. Единственный минус отладки - если это происходит на тестовом или dev стенде - другие люди не смогут там работать. Т.е. их нужно предупредить. Или еще лучше - иметь минимум 2 подходящих стенда. Когда стенд не подходящий - это ПРОМ (PROD) или PROD-like стенды, куда нет сетевого доступа с компьютера разработчика. Вот это пожалуй единственное место, где может понадобиться TRACE. И если он там понадобился, то возможно у вас проблемы либо на этапе тестирования. Либо какие-то значимые отличия в конфигурации стендов, что тоже может быть поводом задуматься.
Что делать с TRACE - убирать сразу после нахождения проблемы, т.е. в следующем хотфиксе.
DEBUG - используется как правило на тестовых стендах чтобы разобраться с пришедшими от смежников данными и как эти данные влияют на состояние нашего сервиса. Разработка и отладка идет на заглушках, что там придет от смежников до конца не ясно, несмотря на согласованную аналитику - вот этот вот кейс. Если используется для трассировки шагов - см. абзац выше)
Что делать с DEBUG - убирать перед фиксацией ПРОМ релиза. Почему бы не оставить? Даже не из-за производительности, этот момент решить можно, а из-за ухудшения читаемости. Размер кода увеличивается, лог как правило дублирует близлежащий метод в плане доносимой информации, т.е. нарушаем DRY. Если DEBUG во внешней библиотеке - отключаем через настройку уровня логирования для конкретного пакета.
Все уровни далее по большому счету пишутся для сопровождения и тестировщиков.
INFO - нет ошибки, но сервис дошел до некой важной точки, и эта информация поможет при разборе полетов в случае ошибок и сбоев. Я видел кейсы, когда сопровождение запрещало писать на ПРОМ с уровнем INFO, но со временем оно одумывалось, и запрет снимали).
WARN - ошибки, не позволяющей вернуть данные клиенту, нет. Но есть либо некритичная ошибка, либо отсутствуют какие-то данные. И поэтому мы либо уходим в альтернативную ветку пользовательского сценария, либо берем данные из кэша, и возвращаем ответ клиенту.
ERROR - ошибка, прокидываемая клиенту. Не в виде stack trace конечно) Суть в том, что процесс обработки клиентского запроса прерывается, возвращается ошибка согласно API. Две самые частые ошибки, что я здесь вижу:
а) вывод error на любую ошибку смежника, даже не блокирующую.
б) error из используемой библиотеки, которая для нашего процесса не является блокирующей. В этом случае ее нужно убирать либо через настройку уровня логирования для пакета этой библиотеки, либо через ее доработку (если это возможно).
Эти три уровня убирать перед релизом не нужно, но стоит периодически их просматривать на предмет актуальности.
И последнее - по производительности. Просадка в производительности из-за логирования может быть из-за вычисления параметров даже в том случае, когда текущий уровень логирования выключен. Спасает ленивое вычисление параметров для выводимого в лог сообщения.
Это поддерживается в
а) log4j https://logging.apache.org/log4j/2.12.x/manual/api.html
б) slf4j https://www.slf4j.org/faq.html#logging_performance
через lambda параметры начиная с Java 8. Т.е. в большинстве инсталляций. Ну я надеюсь на это)
Еще просадка может быть из-за количества сообщений. Тогда смотри абзацы про TRACE и DEBUG выше. Еще можно глянуть мой пост про производительность в log4j https://t.me/javaKotlinDevOps/77 и поднять уровень логирования для ПРОМ.
#java #logging
logging.apache.org
Log4j – Log4j 2 API - Apache Log4j 2
Всем привет!
Есть много способов получить данные из БД с помощью JPA:
1) JPQL
2) JPQL Native Query
3) HQL
4) Spring Data JPA Repository
5) Criteria API
6) QueryDSL
...
Предположим, нам нужно вернуть набор строк. Задать параметры запроса можно по-разному, а итог будет один - List или другая коллекция (Collection) с набором данных. Верно? Не совсем)
Если посмотреть на список возвращаемых Spring Data JPA данных https://docs.spring.io/spring-data/jpa/reference/repositories/query-return-types-reference.html#appendix.query.return.types то там можно увидеть много чего интересного. В т.ч. Stream.
А вот пример его использования: https://vladmihalcea.com/spring-data-jpa-stream/
Аналогично можно вернуть Stream и из обычного JPA - см. метод getResultStream, вот пример: https://thorben-janssen.com/jpa-2-2s-new-stream-method-and-how-you-should-not-use-it/
Зачем это может быть нужно?
Во-первых это просто красиво... Шучу. Если вы используете Stream в бизнес-логике - то кажется логичным использовать их и при обращении к БД.
А во-вторых: главная особенность стриминга - равномерная выборка данных. И в каждый момент данных в обработке будет одна запись.
Рассмотрим кейс, когда нужно обработать на клиенте миллион записей.
Ремарка: если у вас такой кейс - подумайте, нет ли проблем в архитектуре. Данные лучше обрабатывать на сервере СУБД. Если все же проблем нет - продолжим)
Так вот, какие у нас варианты:
1) вытащить на клиент миллион записей. Запрос к БД будет один, она выдержит, но с неплохой вероятностью можно убить клиент через Out of Memory.
2) организовать пагинацию, например, вот так: https://www.baeldung.com/spring-data-jpa-iterate-large-result-sets. Данных на клиенте в моменте не много, по размеру страницы, но запросов к БД ... тысяча.
3) использовать стримы. Запрос к БД один, данных на клиенте немного. Не обязательно одна запись, но в любом случае немного, детали ниже.
К слову, стриминг по БД с JPA аналогичен перемещению курсора по ResultSet в JDBC. С накладными расходами и плюшкам, которые дает сессия JPA, конечно.
И про объем данных на клиенте. Казалось бы - вытаскиваем записи поштучно. Но если не указать fetch size - объём предварительной выборки - для некоторых СУБД Hibernate вытащит на клиента все данные за раз, и мы вернемся к варианту 1 (((
#jpa #java_streams #rdbms
Есть много способов получить данные из БД с помощью JPA:
1) JPQL
2) JPQL Native Query
3) HQL
4) Spring Data JPA Repository
5) Criteria API
6) QueryDSL
...
Предположим, нам нужно вернуть набор строк. Задать параметры запроса можно по-разному, а итог будет один - List или другая коллекция (Collection) с набором данных. Верно? Не совсем)
Если посмотреть на список возвращаемых Spring Data JPA данных https://docs.spring.io/spring-data/jpa/reference/repositories/query-return-types-reference.html#appendix.query.return.types то там можно увидеть много чего интересного. В т.ч. Stream.
А вот пример его использования: https://vladmihalcea.com/spring-data-jpa-stream/
Аналогично можно вернуть Stream и из обычного JPA - см. метод getResultStream, вот пример: https://thorben-janssen.com/jpa-2-2s-new-stream-method-and-how-you-should-not-use-it/
Зачем это может быть нужно?
Во-первых это просто красиво... Шучу. Если вы используете Stream в бизнес-логике - то кажется логичным использовать их и при обращении к БД.
А во-вторых: главная особенность стриминга - равномерная выборка данных. И в каждый момент данных в обработке будет одна запись.
Рассмотрим кейс, когда нужно обработать на клиенте миллион записей.
Ремарка: если у вас такой кейс - подумайте, нет ли проблем в архитектуре. Данные лучше обрабатывать на сервере СУБД. Если все же проблем нет - продолжим)
Так вот, какие у нас варианты:
1) вытащить на клиент миллион записей. Запрос к БД будет один, она выдержит, но с неплохой вероятностью можно убить клиент через Out of Memory.
2) организовать пагинацию, например, вот так: https://www.baeldung.com/spring-data-jpa-iterate-large-result-sets. Данных на клиенте в моменте не много, по размеру страницы, но запросов к БД ... тысяча.
3) использовать стримы. Запрос к БД один, данных на клиенте немного. Не обязательно одна запись, но в любом случае немного, детали ниже.
К слову, стриминг по БД с JPA аналогичен перемещению курсора по ResultSet в JDBC. С накладными расходами и плюшкам, которые дает сессия JPA, конечно.
И про объем данных на клиенте. Казалось бы - вытаскиваем записи поштучно. Но если не указать fetch size - объём предварительной выборки - для некоторых СУБД Hibernate вытащит на клиента все данные за раз, и мы вернемся к варианту 1 (((
#jpa #java_streams #rdbms
Vlad Mihalcea
The best way to use Spring Data JPA Stream methods - Vlad Mihalcea
Learn what is the best way to use Spring Data JPA Stream query methods to avoid prefetching all the data in MySQL and PostgreSQL.
Всем привет!
Не отпускает меня тема AI)
Напомню, что с одной стороны AI ~= Python, но с другой стороны Java потихоньку подтягивается, о чем я уже писал на канале, см. по тегам.
Вот отличный пример генерации данных с помощью AI с запоминанием контекста на Spring AI https://piotrminkowski.com/2025/01/28/getting-started-with-spring-ai-and-chat-model/
Обратите внимание на "магию" Spring - в части преобразования ответа модели в коллекцию.
А вот тут https://piotrminkowski.com/2025/01/30/getting-started-with-spring-ai-function-calling/
на "магию" привязки функций, забирающих данные из API брокера и с сервиса-поставщика биржевой информации к вызову модели.
Красиво, черт возьми!)
P.S. Интересно, учитывая недетерминистическое поведение модели - всегда ли эта магия работает. Буду проверять)
#ai #java #spring
Не отпускает меня тема AI)
Напомню, что с одной стороны AI ~= Python, но с другой стороны Java потихоньку подтягивается, о чем я уже писал на канале, см. по тегам.
Вот отличный пример генерации данных с помощью AI с запоминанием контекста на Spring AI https://piotrminkowski.com/2025/01/28/getting-started-with-spring-ai-and-chat-model/
Обратите внимание на "магию" Spring - в части преобразования ответа модели в коллекцию.
А вот тут https://piotrminkowski.com/2025/01/30/getting-started-with-spring-ai-function-calling/
на "магию" привязки функций, забирающих данные из API брокера и с сервиса-поставщика биржевой информации к вызову модели.
Красиво, черт возьми!)
P.S. Интересно, учитывая недетерминистическое поведение модели - всегда ли эта магия работает. Буду проверять)
#ai #java #spring
Piotr's TechBlog
Getting Started with Spring AI and Chat Model - Piotr's TechBlog
This article will teach you how to use the Spring AI project to build applications based on different chat models.
image_2025-02-20_15-52-56.png
68.4 KB
Всем привет!
Нашел забавную статью о том, как Java превратить в BrainFuck https://ru.wikipedia.org/wiki/Brainfuck
Статья https://habr.com/ru/articles/886080/
Всегда было ощущение, что это на условном Perl можно написать не очень понятную строку кода, выглядящую прилично, а на самом деле форматирующую диск C:) Ну или на Scala на худой конец. А тут Java.
У меня только один вопрос по первому примеру - где код программы, которая подбирала эти магические числа?
А второй отлично показывает, чем может быть вредно Reflection API. Убирать его конечно не надо, но подумать о защите для системных классов стоило бы.
#java
Нашел забавную статью о том, как Java превратить в BrainFuck https://ru.wikipedia.org/wiki/Brainfuck
Статья https://habr.com/ru/articles/886080/
Всегда было ощущение, что это на условном Perl можно написать не очень понятную строку кода, выглядящую прилично, а на самом деле форматирующую диск C:) Ну или на Scala на худой конец. А тут Java.
У меня только один вопрос по первому примеру - где код программы, которая подбирала эти магические числа?
А второй отлично показывает, чем может быть вредно Reflection API. Убирать его конечно не надо, но подумать о защите для системных классов стоило бы.
#java
Wikipedia
Brainfuck
эзотерический язык программирования, придуманный Урбаном Мюллером в 1993 году
Можно ли быстро сделать и запустить single-file прототип на Java?
Как старый джавист я всегда немного с завистью смотрел видео, где человек на Python, PHP, Ruby пишет скрипт, содержащий достаточно сложный код и просто запускает его командой python script.py.
Не сказать, что Java ничего не делает в эту сторону:
1) JShell - позволяет просто запускать Java код без метода main построчно. Работает начиная с Java 9
2) JEP 330: Launch Single-File Source-Code Programs - не надо отдельно вызывать javac. Запуск программы работает через java HelloWorld.java. Работает с Java 11
3) JEP 477 Implicitly Declared Classes and Instance Main Methods - все тоже самое, но теперь не нужно объявлять класс (он создается неявно) и декларация метода main сильно упрощена. В режиме preview с Java 21
Но все это работает с простыми приложениями. А если нужны зависимости? Наш любимый Spring, например, с pom bom и кучей библиотек + Lombok. Да еще в нескольких файлах. Да еще хотелось бы не указывать параметры сборки в командной строке каждый раз, и не плодить лишних shell скриптов.
Когда-то подобная фича была в Spring Boot Cli - да, в Spring Boot и своя консоль. Но фичу выпилили, на мой взгляд зря. И из полезного в Cli остался по сути только аналог Spring Initialzr.
Но я отвлекся)
Кто ищет, тот всегда найдет - встречаем https://www.jbang.dev/documentation/guide/latest/index.html
Как это работает - хорошо проиллюстрировано по ссылке выше.
Я проверил на комбинации Spring Boot + Lombok все работает. Настройки из лежащего рядом application.properties подтягиваются. Единственный момент - были проблемы, если код разнесен по нескольким файлам - не обнаруживалась аннотация @Scheduled. Т.е. реализация multiple source file немного хромает, о чем разработчики предупреждают https://www.jbang.dev/documentation/guide/latest/organizing.html
Зато - все зависимости выкачиваются, код компилируется перед запуском, параметры компиляция настраиваются в файле с исходниками. Если надо - даже выкачивается Java. Принимает на вход java исходники, kotlin, упомянутый выше код для jshell, код внутри markdown (!!!) и можно даже так: jbang --code System.out.println("Hello World!")
Рекомендую к использованию!
#java #prototyping
Как старый джавист я всегда немного с завистью смотрел видео, где человек на Python, PHP, Ruby пишет скрипт, содержащий достаточно сложный код и просто запускает его командой python script.py.
Не сказать, что Java ничего не делает в эту сторону:
1) JShell - позволяет просто запускать Java код без метода main построчно. Работает начиная с Java 9
2) JEP 330: Launch Single-File Source-Code Programs - не надо отдельно вызывать javac. Запуск программы работает через java HelloWorld.java. Работает с Java 11
3) JEP 477 Implicitly Declared Classes and Instance Main Methods - все тоже самое, но теперь не нужно объявлять класс (он создается неявно) и декларация метода main сильно упрощена. В режиме preview с Java 21
Но все это работает с простыми приложениями. А если нужны зависимости? Наш любимый Spring, например, с pom bom и кучей библиотек + Lombok. Да еще в нескольких файлах. Да еще хотелось бы не указывать параметры сборки в командной строке каждый раз, и не плодить лишних shell скриптов.
Когда-то подобная фича была в Spring Boot Cli - да, в Spring Boot и своя консоль. Но фичу выпилили, на мой взгляд зря. И из полезного в Cli остался по сути только аналог Spring Initialzr.
Но я отвлекся)
Кто ищет, тот всегда найдет - встречаем https://www.jbang.dev/documentation/guide/latest/index.html
Как это работает - хорошо проиллюстрировано по ссылке выше.
Я проверил на комбинации Spring Boot + Lombok все работает. Настройки из лежащего рядом application.properties подтягиваются. Единственный момент - были проблемы, если код разнесен по нескольким файлам - не обнаруживалась аннотация @Scheduled. Т.е. реализация multiple source file немного хромает, о чем разработчики предупреждают https://www.jbang.dev/documentation/guide/latest/organizing.html
Зато - все зависимости выкачиваются, код компилируется перед запуском, параметры компиляция настраиваются в файле с исходниками. Если надо - даже выкачивается Java. Принимает на вход java исходники, kotlin, упомянутый выше код для jshell, код внутри markdown (!!!) и можно даже так: jbang --code System.out.println("Hello World!")
Рекомендую к использованию!
#java #prototyping
PHP становится Java-ой?
Недавно посмотрел видео о перспективах PHP https://vkvideo.ru/video-224967259_456239053 Я на PHP писал мало, но т.к. он широко распространён - интересно, развивается ли он и как именно. Он развивается, и я этому не удивлён. Да, есть распространённое негативное мнение про PHP и его разработчиков. Любой простой язык привлекает непрофессионалов. Но ситуация сложнее, чем кажется, на это намекает официальный code style языка https://php-psr.ru/accepted/PSR-12-extended-coding-style-guide/ Обязательность строк разделителей, фиксированное число пробелов, регистр символов - все серьёзно. Небольшое отступление - к аббревиатуре PSR из статьи выше мы ещё вернёмся.
Так вот, PHP становится похожим на Java.
Чтобы понять как именно - стоит вспомнить, чем он характеризовался изначально?
1) динамическая типизация. От неё уходят, с костылями в виде объявления типов в комментариях и проверке статическим анализатором типа checkstyle. Причина - невозможно работать со сложным проектом и динамической типизацией. Если ты конечно не хочешь "гов..кодить".
2) интерпретация вместо компиляции. Тоже уходят, есть AOT и JIT компиляторы PHP. Также фреймворки, например, IoC контейнеры, могут предварительно сохранять конфигурацию на диске. По соображениям производительности
Да, в PHP тоже есть IoC контейнеры.
3) малоизвестный факт - исходно в PHP была т.наз умирающая модель процессов. После обработки запроса клиента контекст процесса полностью очищается. Такой true stateless. Причём очищались не просто клиентские данные, а все бины IoC контейнера, т.е. вообще все. Побочные эффекты такого подхода противоположные. Положительный: PHP компоненты инициализируются очень быстро, по другому никак. Отрицательный: на утечки памяти можно забить, т.е. "гов...кодим") Так вот, от этой модели тоже уходят. Снова по соображениям производительности
В общем язык развивается, т.к. наследие огромное.
P.S. Чтобы не было пренебрежительного отношения к PHP - поговорим про стандартизацию. В PHP есть такое понятие, как middleware. Вот его неплохое описание https://laravel.com/docs/12.x/middleware
Ключевой момент - компоненты middleware переиспользуются в различных фреймворках. Как это получается? Потому что многое стандартизируется, с помощью PSR. В Java тоже есть стандарты - JPA, JDBC, http сервлеты и фильтры, но видится, что их меньше. Когда-то этим занимался проект Java EE, не смог, умер и воскрес, а за это время возникло несколько экосистем - Spring, Quarkus, Micronaut... И это я Kotlin не беру) Почему я назвал их экосистемами - каждая старается привязать к своим компонентам. Так что как ни странно - PHP выглядит более стандартизированным)
#java #PHP #lang
Недавно посмотрел видео о перспективах PHP https://vkvideo.ru/video-224967259_456239053 Я на PHP писал мало, но т.к. он широко распространён - интересно, развивается ли он и как именно. Он развивается, и я этому не удивлён. Да, есть распространённое негативное мнение про PHP и его разработчиков. Любой простой язык привлекает непрофессионалов. Но ситуация сложнее, чем кажется, на это намекает официальный code style языка https://php-psr.ru/accepted/PSR-12-extended-coding-style-guide/ Обязательность строк разделителей, фиксированное число пробелов, регистр символов - все серьёзно. Небольшое отступление - к аббревиатуре PSR из статьи выше мы ещё вернёмся.
Так вот, PHP становится похожим на Java.
Чтобы понять как именно - стоит вспомнить, чем он характеризовался изначально?
1) динамическая типизация. От неё уходят, с костылями в виде объявления типов в комментариях и проверке статическим анализатором типа checkstyle. Причина - невозможно работать со сложным проектом и динамической типизацией. Если ты конечно не хочешь "гов..кодить".
2) интерпретация вместо компиляции. Тоже уходят, есть AOT и JIT компиляторы PHP. Также фреймворки, например, IoC контейнеры, могут предварительно сохранять конфигурацию на диске. По соображениям производительности
Да, в PHP тоже есть IoC контейнеры.
3) малоизвестный факт - исходно в PHP была т.наз умирающая модель процессов. После обработки запроса клиента контекст процесса полностью очищается. Такой true stateless. Причём очищались не просто клиентские данные, а все бины IoC контейнера, т.е. вообще все. Побочные эффекты такого подхода противоположные. Положительный: PHP компоненты инициализируются очень быстро, по другому никак. Отрицательный: на утечки памяти можно забить, т.е. "гов...кодим") Так вот, от этой модели тоже уходят. Снова по соображениям производительности
В общем язык развивается, т.к. наследие огромное.
P.S. Чтобы не было пренебрежительного отношения к PHP - поговорим про стандартизацию. В PHP есть такое понятие, как middleware. Вот его неплохое описание https://laravel.com/docs/12.x/middleware
Ключевой момент - компоненты middleware переиспользуются в различных фреймворках. Как это получается? Потому что многое стандартизируется, с помощью PSR. В Java тоже есть стандарты - JPA, JDBC, http сервлеты и фильтры, но видится, что их меньше. Когда-то этим занимался проект Java EE, не смог, умер и воскрес, а за это время возникло несколько экосистем - Spring, Quarkus, Micronaut... И это я Kotlin не беру) Почему я назвал их экосистемами - каждая старается привязать к своим компонентам. Так что как ни странно - PHP выглядит более стандартизированным)
#java #PHP #lang
VK Видео
Какое будущее ждет PHP? / Валентин Удальцов / #14
В этом выпуске мы вместе с Валентином Удальцовым, автором канала Пых в Telegram, обсуждаем PHP (тот самый язык программирования, про который говорят, что он умирает, а на нём 80% сайтов до сих пор написано). Поговорим про весь путь его развития — от старых…