(java || kotlin) && devOps
356 subscribers
7 photos
1 video
7 files
354 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Небольшое дополнение по 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
👍2
Всем привет!

Давно не писал про 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
🔥1
Всем привет!

Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #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
👍1
Всем привет!

Давно хотел написать небольшую памятку по уровням логирования.

Стандартный набор уровней логирования для бизнес приложений:
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